Skip to content

Commit

Permalink
Add Atom.animated
Browse files Browse the repository at this point in the history
  • Loading branch information
zenith391 committed May 17, 2023
1 parent 55be9c6 commit c009151
Showing 1 changed file with 66 additions and 2 deletions.
68 changes: 66 additions & 2 deletions src/data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub fn lerp(a: anytype, b: @TypeOf(a), t: f64) @TypeOf(a) {
}
const lerpInt = lerp;

pub const Easing = *const fn (t: f64) f64;
pub const Easings = struct {
pub fn Linear(t: f64) f64 {
return t;
Expand Down Expand Up @@ -145,7 +146,11 @@ pub fn Atom(comptime T: type) type {
const isAnimable = isAnimableType(T);

pub const ValueType = T;
pub const ChangeListener = struct { function: *const fn (newValue: T, userdata: usize) void, userdata: usize = 0 };
pub const ChangeListener = struct {
function: *const fn (newValue: T, userdata: usize) void,
userdata: usize = 0,
type: enum { Change, Destroy } = .Change,
};
pub const Binding = struct {
bound_to: *Self,
link_id: u16,
Expand Down Expand Up @@ -181,6 +186,48 @@ pub fn Atom(comptime T: type) type {
return ptr;
}

/// Allocates a new atom and make it follow the value of the original atom, albeit
/// with an animation.
pub fn animated(original: *Self, easing: Easing, duration: u64) !*Self {
var self = Self.alloc(original.get());

const AnimationParameters = struct {
easing: Easing,
duration: u64,
self_ptr: *Self,
};

const userdata = try self.allocator.?.create(AnimationParameters);
userdata.* = .{ .easing = easing, .duration = duration, .self_ptr = self };

const animate_fn = struct {
fn a(new_value: T, int: usize) void {
const ptr = @intToPtr(*AnimationParameters, int);
ptr.self_ptr.animate(ptr.easing, new_value, ptr.duration);
}
}.a;

const destroy_fn = struct {
fn a(_: T, int: usize) void {
const ptr = @intToPtr(*AnimationParameters, int);
const allocator = lasting_allocator;
allocator.destroy(ptr);
}
}.a;

_ = try original.addChangeListener(.{
.function = animate_fn,
.userdata = @ptrToInt(userdata),
.type = .Change,
});
_ = try original.addChangeListener(.{
.function = destroy_fn,
.userdata = @ptrToInt(userdata),
.type = .Destroy,
});
return self;
}

/// This function updates any current animation.
/// It returns true if the animation isn't done, false otherwises.
pub fn update(self: *Self) bool {
Expand Down Expand Up @@ -488,7 +535,9 @@ pub fn Atom(comptime T: type) type {
var nullableNode = self.onChange.first;
const value = self.get();
while (nullableNode) |node| {
node.data.function(value, node.data.userdata);
if (node.data.type == .Change) {
node.data.function(value, node.data.userdata);
}
nullableNode = node.next;
}
}
Expand All @@ -497,6 +546,9 @@ pub fn Atom(comptime T: type) type {
var nullableNode = self.onChange.first;
while (nullableNode) |node| {
nullableNode = node.next;
if (node.data.type == .Destroy) {
node.data.function(undefined, node.data.userdata);
}
lasting_allocator.destroy(node);
}
if (self.allocator) |allocator| {
Expand Down Expand Up @@ -767,3 +819,15 @@ test "format atom" {
dataSource4.set(42);
try std.testing.expectEqualStrings("10 and 42", format2.get());
}

test "animated atom" {
var original = Atom(i32).of(0);
defer original.deinit();

var animated = try Atom(i32).animated(&original, Easings.Linear, 1000);
defer animated.deinit();
defer _animatedAtoms.clearAndFree();

original.set(1000);
try std.testing.expect(animated.hasAnimation());
}

0 comments on commit c009151

Please sign in to comment.