mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 13:51:13 +00:00
Implement property animations for C++
This commit is contained in:
parent
f50a705e00
commit
e6b386ab53
4 changed files with 241 additions and 32 deletions
|
@ -1,5 +1,12 @@
|
|||
#pragma once
|
||||
#include <string_view>
|
||||
|
||||
namespace sixtyfps {
|
||||
namespace internal {
|
||||
struct PropertyAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
#include "sixtyfps_properties_internal.h"
|
||||
|
||||
namespace sixtyfps {
|
||||
|
@ -41,8 +48,56 @@ struct Property
|
|||
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); });
|
||||
}
|
||||
|
||||
inline void set_animated_value(const T &value,
|
||||
const internal::PropertyAnimation &animation_data);
|
||||
template<typename F>
|
||||
inline void set_animated_binding(F binding, const internal::PropertyAnimation &animation_data);
|
||||
|
||||
private:
|
||||
internal::PropertyHandleOpaque inner;
|
||||
mutable T value{};
|
||||
};
|
||||
|
||||
template<>
|
||||
void Property<int32_t>::set_animated_value(const int32_t &value,
|
||||
const internal::PropertyAnimation &animation_data)
|
||||
{
|
||||
internal::sixtyfps_property_set_animated_value_int(&inner, value, &animation_data);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Property<float>::set_animated_value(const float &value,
|
||||
const internal::PropertyAnimation &animation_data)
|
||||
{
|
||||
internal::sixtyfps_property_set_animated_value_float(&inner, value, &animation_data);
|
||||
}
|
||||
|
||||
template<>
|
||||
template<typename F>
|
||||
void Property<int32_t>::set_animated_binding(F binding,
|
||||
const internal::PropertyAnimation &animation_data)
|
||||
{
|
||||
internal::sixtyfps_property_set_animated_binding_int(
|
||||
&inner,
|
||||
[](void *user_data, const internal::EvaluationContext *context, int32_t *value) {
|
||||
*reinterpret_cast<int32_t *>(value) = (*reinterpret_cast<F *>(user_data))(context);
|
||||
},
|
||||
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
|
||||
&animation_data);
|
||||
}
|
||||
|
||||
template<>
|
||||
template<typename F>
|
||||
void Property<float>::set_animated_binding(F binding,
|
||||
const internal::PropertyAnimation &animation_data)
|
||||
{
|
||||
internal::sixtyfps_property_set_animated_binding_float(
|
||||
&inner,
|
||||
[](void *user_data, const internal::EvaluationContext *context, float *value) {
|
||||
*reinterpret_cast<float *>(value) = (*reinterpret_cast<F *>(user_data))(context);
|
||||
},
|
||||
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
|
||||
&animation_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -153,6 +153,68 @@ impl CppType for Type {
|
|||
}
|
||||
}
|
||||
|
||||
fn property_animation_code(
|
||||
component: &Rc<Component>,
|
||||
element: &Element,
|
||||
property_name: &str,
|
||||
) -> Option<String> {
|
||||
if let Some(animation) = element.property_animations.get(property_name) {
|
||||
let bindings: Vec<String> = animation
|
||||
.borrow()
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|(prop, initializer)| {
|
||||
let initializer = compile_expression(initializer, component);
|
||||
format!("animation.{} = {};", prop, initializer)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(format!(
|
||||
r#"[](){{
|
||||
sixtyfps::internal::PropertyAnimation animation{{}};
|
||||
{}
|
||||
return animation;
|
||||
}}()"#,
|
||||
bindings.join("\n")
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn property_set_value_code(
|
||||
component: &Rc<Component>,
|
||||
element: &Element,
|
||||
property_name: &str,
|
||||
value_expr: String,
|
||||
) -> String {
|
||||
if let Some(animation_code) = property_animation_code(component, element, property_name) {
|
||||
format!(
|
||||
"set_animated_value({value}, {animation})",
|
||||
value = value_expr,
|
||||
animation = animation_code
|
||||
)
|
||||
} else {
|
||||
format!("set({})", value_expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn property_set_binding_code(
|
||||
component: &Rc<Component>,
|
||||
element: &Element,
|
||||
property_name: &str,
|
||||
binding_expr: String,
|
||||
) -> String {
|
||||
if let Some(animation_code) = property_animation_code(component, element, property_name) {
|
||||
format!(
|
||||
"set_animated_binding({binding}, {animation})",
|
||||
binding = binding_expr,
|
||||
animation = animation_code
|
||||
)
|
||||
} else {
|
||||
format!("set_binding({})", binding_expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>) {
|
||||
main_struct.members.push(Declaration::Var(Var {
|
||||
ty: format!("sixtyfps::{}", item.base_type.as_builtin().class_name),
|
||||
|
@ -187,26 +249,34 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
|
|||
format!("{id}.", id = id.clone())
|
||||
};
|
||||
|
||||
let init = compile_expression(i, &item.enclosing_component.upgrade().unwrap());
|
||||
let component = &item.enclosing_component.upgrade().unwrap();
|
||||
|
||||
let init = compile_expression(i, component);
|
||||
if i.is_constant() {
|
||||
let setter = property_set_value_code(&component, item, s, init);
|
||||
format!(
|
||||
"{accessor_prefix}{cpp_prop}.set({init});",
|
||||
"{accessor_prefix}{cpp_prop}.{setter};",
|
||||
accessor_prefix = accessor_prefix,
|
||||
cpp_prop = s,
|
||||
init = init
|
||||
setter = setter
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{accessor_prefix}{cpp_prop}.set_binding(
|
||||
[]([[maybe_unused]] const sixtyfps::EvaluationContext *context) {{
|
||||
let binding_code = format!(
|
||||
"[]([[maybe_unused]] const sixtyfps::EvaluationContext *context) {{
|
||||
[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(context->component.instance);
|
||||
return {init};
|
||||
}}
|
||||
);",
|
||||
accessor_prefix = accessor_prefix,
|
||||
cpp_prop = s,
|
||||
}}",
|
||||
ty = main_struct.name,
|
||||
init = init
|
||||
);
|
||||
|
||||
let binding_setter = property_set_binding_code(component, item, s, binding_code);
|
||||
|
||||
format!(
|
||||
"{accessor_prefix}{cpp_prop}.{binding_setter};",
|
||||
accessor_prefix = accessor_prefix,
|
||||
cpp_prop = s,
|
||||
binding_setter = binding_setter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -399,6 +399,34 @@ pub unsafe extern "C" fn sixtyfps_property_set_changed(out: *const PropertyHandl
|
|||
inner.borrow_mut().binding = None;
|
||||
}
|
||||
|
||||
fn make_c_function_binding<T: 'static>(
|
||||
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut T),
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
) -> Rc<dyn Binding<T>> {
|
||||
struct CFunctionBinding<T> {
|
||||
binding_function: extern "C" fn(*mut c_void, &EvaluationContext, *mut T),
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
}
|
||||
|
||||
impl<T> Drop for CFunctionBinding<T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(x) = self.drop_user_data {
|
||||
x(self.user_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Binding<T> for CFunctionBinding<T> {
|
||||
fn evaluate(self: Rc<Self>, value_ptr: &mut T, context: &EvaluationContext) {
|
||||
(self.binding_function)(self.user_data, context, value_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
Rc::new(CFunctionBinding { binding_function: binding, user_data, drop_user_data })
|
||||
}
|
||||
|
||||
/// Set a binding
|
||||
/// The binding has signature fn(user_data, context, pointer_to_value)
|
||||
///
|
||||
|
@ -416,28 +444,7 @@ pub unsafe extern "C" fn sixtyfps_property_set_binding(
|
|||
) {
|
||||
let inner = &*(out as *const PropertyHandle<()>);
|
||||
|
||||
struct CFunctionBinding {
|
||||
binding_function: extern "C" fn(*mut c_void, &EvaluationContext, *mut c_void),
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
}
|
||||
|
||||
impl Drop for CFunctionBinding {
|
||||
fn drop(&mut self) {
|
||||
if let Some(x) = self.drop_user_data {
|
||||
x(self.user_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Binding<()> for CFunctionBinding {
|
||||
fn evaluate(self: Rc<Self>, value_ptr: &mut (), context: &EvaluationContext) {
|
||||
(self.binding_function)(self.user_data, context, value_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
let binding =
|
||||
Rc::new(CFunctionBinding { binding_function: binding, user_data, drop_user_data });
|
||||
let binding = make_c_function_binding(binding, user_data, drop_user_data);
|
||||
|
||||
inner.borrow_mut().binding = Some(binding);
|
||||
inner.clone().mark_dirty(DirtyReason::ValueOrDependencyHasChanged);
|
||||
|
@ -626,6 +633,82 @@ impl<T: InterpolatedPropertyValue> Drop for PropertyAnimationBinding<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Internal function to set up a property animation to the specified target value for an integer property.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_property_set_animated_value_int(
|
||||
out: *const PropertyHandleOpaque,
|
||||
value: i32,
|
||||
animation_data: &crate::abi::primitives::PropertyAnimation,
|
||||
) {
|
||||
let inner = &*(out as *const PropertyHandle<i32>);
|
||||
let animation = Rc::new(RefCell::new(PropertyAnimationBinding::new_with_value(
|
||||
value,
|
||||
animation_data,
|
||||
inner.clone(),
|
||||
)));
|
||||
|
||||
PropertyImpl::set_binding(inner.clone(), Some(animation.clone()));
|
||||
}
|
||||
|
||||
/// Internal function to set up a property animation to the specified target value for a float property.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_property_set_animated_value_float(
|
||||
out: *const PropertyHandleOpaque,
|
||||
value: f32,
|
||||
animation_data: &crate::abi::primitives::PropertyAnimation,
|
||||
) {
|
||||
let inner = &*(out as *const PropertyHandle<f32>);
|
||||
let animation = Rc::new(RefCell::new(PropertyAnimationBinding::new_with_value(
|
||||
value,
|
||||
animation_data,
|
||||
inner.clone(),
|
||||
)));
|
||||
|
||||
PropertyImpl::set_binding(inner.clone(), Some(animation.clone()));
|
||||
}
|
||||
|
||||
/// Internal function to set up a property animation between values produced by the specified binding for an integer property.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_int(
|
||||
out: *const PropertyHandleOpaque,
|
||||
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut i32),
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
animation_data: &crate::abi::primitives::PropertyAnimation,
|
||||
) {
|
||||
let inner = &*(out as *const PropertyHandle<i32>);
|
||||
|
||||
let binding = make_c_function_binding(binding, user_data, drop_user_data);
|
||||
|
||||
let animation = Rc::new(RefCell::new(PropertyAnimationBinding::new_with_binding(
|
||||
binding,
|
||||
animation_data,
|
||||
inner.clone(),
|
||||
)));
|
||||
PropertyImpl::set_binding(inner.clone(), Some(animation.clone()));
|
||||
}
|
||||
|
||||
/// Internal function to set up a property animation between values produced by the specified binding for a float property.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float(
|
||||
out: *const PropertyHandleOpaque,
|
||||
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut f32),
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
animation_data: &crate::abi::primitives::PropertyAnimation,
|
||||
) {
|
||||
let inner = &*(out as *const PropertyHandle<f32>);
|
||||
|
||||
let binding = make_c_function_binding(binding, user_data, drop_user_data);
|
||||
|
||||
let animation = Rc::new(RefCell::new(PropertyAnimationBinding::new_with_binding(
|
||||
binding,
|
||||
animation_data,
|
||||
inner.clone(),
|
||||
)));
|
||||
PropertyImpl::set_binding(inner.clone(), Some(animation.clone()));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -12,6 +12,7 @@ fn main() {
|
|||
"ComponentVTable",
|
||||
"Slice",
|
||||
"ComponentWindowOpaque",
|
||||
"PropertyAnimation",
|
||||
]
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue