Add support for animating color properties

This commit is contained in:
Simon Hausmann 2020-06-27 16:51:48 +02:00
parent be75cb2b21
commit b8ca0fe3c9
9 changed files with 91 additions and 2 deletions

View file

@ -1,6 +1,7 @@
#pragma once
#include "sixtyfps_color_internal.h"
#include "sixtyfps_properties.h"
#include <stdint.h>
@ -30,4 +31,25 @@ private:
internal::types::Color inner;
};
template<>
void Property<Color>::set_animated_value(const Color &value,
const internal::PropertyAnimation &animation_data)
{
internal::sixtyfps_property_set_animated_value_color(&inner, &value, &animation_data);
}
template<>
template<typename F>
void Property<Color>::set_animated_binding(F binding,
const internal::PropertyAnimation &animation_data)
{
internal::sixtyfps_property_set_animated_binding_color(
&inner,
[](void *user_data, const internal::EvaluationContext *context, Color *value) {
*reinterpret_cast<Color *>(value) = (*reinterpret_cast<F *>(user_data))(context);
},
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data);
}
}

View file

@ -25,6 +25,7 @@ component ButtonRectangle := Rectangle {
width: 100;
height: 75;
color: button_area.pressed ? red : button_color;
animate color { duration: 200; }
button_area := TouchArea {
width: root.width;
height: root.height;

View file

@ -40,6 +40,7 @@ component ButtonRectangle := Rectangle {
color: black;
}
color: { button_area.pressed ? red : #5898; }
animate color { duration: 500; }
animate x {
duration: 200;
}

View file

@ -208,6 +208,7 @@ impl TypeRegister {
r.property_animation_type = Type::Builtin(Rc::new(property_animation));
r.supported_property_animation_types.insert(Type::Float32.to_string());
r.supported_property_animation_types.insert(Type::Int32.to_string());
r.supported_property_animation_types.insert(Type::Color.to_string());
r
}

View file

@ -210,6 +210,23 @@ impl From<u32> for Color {
}
}
impl crate::abi::properties::InterpolatedPropertyValue for Color {
fn interpolate(self, target_value: Self, t: f32) -> Self {
Self {
red: self.red.interpolate(target_value.red, t),
green: self.green.interpolate(target_value.green, t),
blue: self.blue.interpolate(target_value.blue, t),
alpha: self.alpha.interpolate(target_value.alpha, t),
}
}
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "argb({}, {}, {}, {})", self.alpha, self.red, self.green, self.blue)
}
}
/// A resource is a reference to binary data, for example images. They can be accessible on the file
/// system or embedded in the resulting binary. Or they might be URLs to a web server and a downloaded
/// is necessary before they can be used.

View file

@ -5,6 +5,7 @@
thin dst container, and intrusive linked list
*/
use crate::abi::datastructures::Color;
use crate::abi::primitives::PropertyAnimation;
use crate::ComponentRefPin;
use core::cell::*;
@ -485,6 +486,12 @@ impl InterpolatedPropertyValue for i32 {
}
}
impl InterpolatedPropertyValue for u8 {
fn interpolate(self, target_value: Self, t: f32) -> Self {
((self as f32) + (t * ((target_value as f32) - (self as f32)))).min(255.).max(0.) as u8
}
}
#[derive(Default)]
/// PropertyAnimationBinding provides a linear animation of values of a property, when they are changed
/// through bindings or direct set() calls.
@ -672,6 +679,23 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_value_float(
PropertyImpl::set_binding(inner.clone(), Some(animation.clone()));
}
/// Internal function to set up a property animation to the specified target value for a color property.
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_value_color(
out: *const PropertyHandleOpaque,
value: &Color,
animation_data: &crate::abi::primitives::PropertyAnimation,
) {
let inner = &*(out as *const PropertyHandle<Color>);
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(
@ -714,6 +738,27 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float(
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 color property.
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_color(
out: *const PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut Color),
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<Color>);
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::*;

View file

@ -59,6 +59,7 @@ fn main() {
.with_config(config.clone())
.with_src(crate_dir.join("abi/properties.rs"))
.with_src(crate_dir.join("abi/signals.rs"))
.with_after_include("namespace sixtyfps { struct Color; }")
.generate()
.expect("Unable to generate bindings")
.write_to_file(include_dir.join("sixtyfps_properties_internal.h"));

View file

@ -269,7 +269,7 @@ fn generate_component(
Type::Float32 => animated_property_info::<f32>(),
Type::Int32 => animated_property_info::<i32>(),
Type::String => property_info::<SharedString>(),
Type::Color => property_info::<Color>(),
Type::Color => animated_property_info::<Color>(),
Type::Resource => property_info::<Resource>(),
Type::Bool => property_info::<bool>(),
Type::Signal => {

View file

@ -25,7 +25,8 @@ component ButtonRectangle := Rectangle {
height: 75;
inner := Rectangle {
color: root.color;
color: { area.pressed ? green : root.color };
animate color { duration: 500; }
area := TouchArea {
width: inner.width;
height: inner.height;