use super::animations::{ICompositionAnimation, ICompositionAnimationBase, ImplicitAnimationCollection};
use super::expressions::ExpressionVariant;
use super::server::{CompositionProperty, ServerObjectId};
use super::{CompositionPropertySet, Compositor, ICompositorSerializable, PendingAnimations};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
pub struct CompositionObject {
compositor: Rc<Compositor>,
server: Option<ServerObjectId>,
is_disposed: Cell<bool>,
registered_for_serialization: Cell<bool>,
pending_animations: PendingAnimations,
implicit_animations: RefCell<Option<Rc<ImplicitAnimationCollection>>>,
}
impl CompositionObject {
pub fn new(compositor: &Rc<Compositor>, server: Option<ServerObjectId>) -> Self {
Self {
compositor: compositor.clone(),
server,
is_disposed: Cell::new(false),
registered_for_serialization: Cell::new(false),
pending_animations: PendingAnimations::new(),
implicit_animations: RefCell::new(None),
}
}
pub fn implicit_animations(&self) -> Option<Rc<ImplicitAnimationCollection>> {
self.implicit_animations.borrow().clone()
}
pub fn set_implicit_animations(&self, value: Option<Rc<ImplicitAnimationCollection>>) {
*self.implicit_animations.borrow_mut() = value;
}
pub fn implicit_animation(&self, property_name: &str) -> Option<Rc<dyn ICompositionAnimationBase>> {
let animations = self.implicit_animations.borrow().clone()?;
animations.try_get_value(property_name)
}
pub fn compositor(&self) -> &Rc<Compositor> {
&self.compositor
}
pub fn server(&self) -> Option<ServerObjectId> {
self.server
}
pub fn required_server(&self) -> ServerObjectId {
match self.server {
Some(server) => server,
None => panic!("There is no server-side counterpart for this object"),
}
}
pub fn try_get_server(&self, c: &Compositor) -> Option<ServerObjectId> {
debug_assert!(std::ptr::eq(c, &*self.compositor));
if self.is_disposed.get() {
return None;
}
Some(self.required_server())
}
pub fn is_disposed(&self) -> bool {
self.is_disposed.get()
}
pub fn pending_animations(&self) -> &PendingAnimations {
&self.pending_animations
}
pub fn dispose(&self) {
if !self.is_disposed.get() {
if let Some(server) = self.server {
self.compositor.dispose_and_keep_on_next_batch(server);
}
}
self.is_disposed.set(true);
}
pub(crate) fn mark_disposed(&self) {
self.is_disposed.set(true);
}
pub fn register_for_serialization(&self, this: impl FnOnce() -> Option<Rc<dyn ICompositorSerializable>>) {
if self.server.is_none() {
panic!("The object doesn't have an associated server counterpart");
}
if self.registered_for_serialization.get() || self.is_disposed.get() {
return;
}
if let Some(this) = this() {
self.registered_for_serialization.set(true);
self.compositor.register_for_serialization(this);
}
}
pub fn begin_serialize_changes(&self, c: &Compositor) {
debug_assert!(std::ptr::eq(c, &*self.compositor));
self.registered_for_serialization.set(false);
}
pub fn stop_animation(&self, property: &'static CompositionProperty) {
let Some(server) = self.server else { return };
self.compositor.post_server_job(
move |compositor| {
if let Some(object) = compositor.get_animated_object(server) {
if let Some(animations) = object.server_object().animations() {
animations.remove_animation_for_property(property);
}
}
},
false,
);
}
}
impl Drop for CompositionObject {
fn drop(&mut self) {
if let Some(server) = self.server {
if self.is_disposed.get() {
self.compositor.release_with_a_later_batch(server);
} else {
self.compositor.dispose_with_a_later_batch(server);
}
}
}
}
pub trait AsCompositionObject: 'static {
fn as_composition_object(&self) -> &CompositionObject;
fn composition_type_name(&self) -> &'static str;
fn as_property_set(self: Rc<Self>) -> Option<Rc<CompositionPropertySet>> {
None
}
}
pub trait ICompositionObjectAnimations {
fn try_start_animation(
&self,
property_name: &str,
animation: &dyn ICompositionAnimation,
final_value: Option<ExpressionVariant>,
) -> bool;
fn get_composition_property(&self, property_name: &str) -> Option<&'static CompositionProperty>;
fn composition_object(&self) -> &CompositionObject;
fn start_animation(&self, property_name: &str, animation: &dyn ICompositionAnimation) {
self.start_animation_with_final_value(property_name, animation, None)
}
fn start_animation_with_final_value(
&self,
property_name: &str,
animation: &dyn ICompositionAnimation,
final_value: Option<ExpressionVariant>,
) {
if !self.try_start_animation(property_name, animation, final_value) {
panic!("Unknown property {property_name}");
}
}
fn stop_animation(&self, property_name: &str) {
let Some(property) = self.get_composition_property(property_name) else {
panic!("Unknown property {property_name}");
};
self.composition_object().stop_animation(property);
}
fn start_animation_group(&self, grp: &dyn ICompositionAnimationBase) {
if let Some(animation) = grp.as_composition_animation() {
let Some(target) = animation.target() else { panic!("Animation Target can't be null") };
self.start_animation(&target, animation);
} else if let Some(group) = grp.as_composition_animation_group() {
for a in group.animations() {
let Some(target) = a.target() else { panic!("Animation Target can't be null") };
self.start_animation(&target, &*a);
}
}
}
fn start_animation_group_part(
&self,
animation: &dyn ICompositionAnimation,
target: &str,
final_value: ExpressionVariant,
) -> bool {
let Some(animation_target) = animation.target() else { panic!("Animation Target can't be null") };
if animation_target == target {
self.start_animation_with_final_value(&animation_target, animation, Some(final_value));
true
} else {
self.start_animation(&animation_target, animation);
false
}
}
fn start_animation_group_for(
&self,
grp: &dyn ICompositionAnimationBase,
target: &str,
final_value: ExpressionVariant,
) -> bool {
if let Some(animation) = grp.as_composition_animation() {
return self.start_animation_group_part(animation, target, final_value);
}
if let Some(group) = grp.as_composition_animation_group() {
let mut matched = false;
for a in group.animations() {
if a.target().is_none() {
panic!("Animation Target can't be null");
}
if self.start_animation_group_part(&*a, target, final_value) {
matched = true;
}
}
return matched;
}
panic!("the animation is neither an animation nor a group of animations");
}
fn stop_animation_group(&self, grp: &dyn ICompositionAnimationBase) {
if let Some(animation) = grp.as_composition_animation() {
let Some(target) = animation.target() else { panic!("Animation Target can't be null") };
self.stop_animation(&target);
} else if let Some(group) = grp.as_composition_animation_group() {
for a in group.animations() {
let Some(target) = a.target() else { panic!("Animation Target can't be null") };
self.stop_animation(&target);
}
}
}
}