use super::{IAnimationInstance, PropertySetSnapshot};
use crate::rendering::composition::expressions::{ExpressionKeywords, IExpressionObject};
use crate::rendering::composition::server::{
CompositionProperty, IAnimatedServerObject, IServerClockItem, ServerCompositor, ServerExpressionObject,
ServerObjectAnimations, ServerObjectId,
};
use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::rc::{Rc, Weak};
pub struct AnimationInstanceBase {
this: Weak<dyn IAnimationInstance>,
tracked_objects: RefCell<Option<Vec<(Weak<dyn IAnimatedServerObject>, &'static CompositionProperty)>>>,
parameters: Rc<PropertySetSnapshot>,
target_object: ServerObjectId,
target: RefCell<Option<Weak<dyn IAnimatedServerObject>>>,
compositor: RefCell<Weak<ServerCompositor>>,
property: Cell<Option<&'static CompositionProperty>>,
invalidated: Cell<bool>,
}
impl AnimationInstanceBase {
pub fn new(this: Weak<dyn IAnimationInstance>, target: ServerObjectId, parameters: Rc<PropertySetSnapshot>) -> Self {
Self {
this,
tracked_objects: RefCell::new(None),
parameters,
target_object: target,
target: RefCell::new(None),
compositor: RefCell::new(Weak::new()),
property: Cell::new(None),
invalidated: Cell::new(false),
}
}
pub fn parameters(&self) -> &Rc<PropertySetSnapshot> {
&self.parameters
}
pub fn target_object(&self) -> ServerObjectId {
self.target_object
}
pub fn target(&self) -> Option<Rc<dyn IAnimatedServerObject>> {
self.target.borrow().as_ref().and_then(Weak::upgrade)
}
pub fn compositor(&self) -> Option<Rc<ServerCompositor>> {
self.compositor.borrow().upgrade()
}
pub fn property(&self) -> Option<&'static CompositionProperty> {
self.property.get()
}
pub fn this_clock_item(&self) -> Option<Rc<dyn IServerClockItem>> {
self.this.upgrade().map(|this| this as Rc<dyn IServerClockItem>)
}
pub fn resolve(&self, compositor: &Rc<ServerCompositor>) {
*self.compositor.borrow_mut() = Rc::downgrade(compositor);
*self.target.borrow_mut() =
compositor.get_animated_object(self.target_object).map(|target| Rc::downgrade(&target));
self.parameters.resolve(compositor);
}
pub fn with_target_expression_object<R>(&self, f: impl FnOnce(Option<&dyn IExpressionObject>) -> R) -> R {
match self.target() {
Some(target) => {
let target = ServerExpressionObject(target);
f(Some(&target))
}
None => f(None),
}
}
pub fn initialize(&self, property: &'static CompositionProperty, tracked_objects: &HashSet<(String, String)>) {
if !tracked_objects.is_empty() {
let mut tracked_list = Vec::new();
for (name, member) in tracked_objects {
let obj = if name == ExpressionKeywords::TARGET {
self.target()
} else {
self.parameters.get_server_object_parameter(name)
};
if let Some(tracked) = obj {
let Some(off) = tracked.get_composition_property(member) else {
if cfg!(debug_assertions) {
panic!("Attempting to subscribe to unknown field");
}
continue;
};
tracked_list.push((Rc::downgrade(&tracked), off));
}
}
*self.tracked_objects.borrow_mut() = Some(tracked_list);
}
self.property.set(Some(property));
}
pub fn begin_evaluate(&self) {
self.invalidated.set(false);
}
fn tracked(&self) -> Vec<(Rc<dyn IAnimatedServerObject>, &'static CompositionProperty)> {
self.tracked_objects
.borrow()
.iter()
.flatten()
.filter_map(|(obj, member)| obj.upgrade().map(|obj| (obj, *member)))
.collect()
}
pub fn activate(&self) {
let Some(this) = self.this.upgrade() else { return };
for (obj, member) in self.tracked() {
obj.server_object().get_or_create_animations().subscribe_to_invalidation(member, this.clone());
}
}
pub fn deactivate(&self) {
let Some(this) = self.this.upgrade() else { return };
for (obj, member) in self.tracked() {
if let Some(animations) = obj.server_object().animations() {
animations.unsubscribe_from_invalidation(member, &this);
}
}
}
pub fn invalidate(&self) {
if self.invalidated.get() {
return;
}
self.invalidated.set(true);
let (Some(target), Some(property)) = (self.target(), self.property.get()) else { return };
if let Some(animations) = target.server_object().animations() {
ServerObjectAnimations::notify_animation_instance_invalidated(&animations, property);
}
}
pub fn on_tick(&self) {
self.invalidate()
}
}