Skip to main content

ferroui_base/media/
transform.rs

1use crate::animation::Animatable;
2use crate::media::immutable::ImmutableTransform;
3use crate::media::MatrixTransform;
4use crate::media::ref_adapter::RefAdapter;
5use crate::reactive::{Disposable, IDisposable};
6use crate::rendering::composition::drawing::{CompositorResourceHolder, ICompositionRenderResource};
7use crate::rendering::composition::generated::ServerCompositionSimpleTransformProps;
8use crate::rendering::composition::server::{IServerObject, ServerCompositionSimpleTransform, ServerObjectId};
9use crate::rendering::composition::transport::BatchStreamWriter;
10use crate::rendering::composition::{Compositor, ICompositorSerializable};
11use crate::utilities::{FormatError, HandlerList};
12use crate::{ferro_class, FerroObject, FerroObjectImpl, Matrix, Ref, Upcast};
13use std::fmt;
14use std::rc::Rc;
15
16/// Represents a transform on a visual or brush. Abstract base class of the
17/// mutable transforms.
18#[repr(C)]
19pub struct Transform {
20    base: Animatable,
21    changed: HandlerList<dyn Fn()>,
22    resource: CompositorResourceHolder,
23}
24
25ferro_class! {
26    Transform: Animatable, virtuals TransformImpl: FerroObjectImpl {
27        /// The transform's matrix.
28        fn value(this) -> Matrix;
29    }
30}
31crate::ferro_class_info!(Transform { interfaces: [std::rc::Rc<dyn crate::media::ITransform>] });
32
33crate::ferro_impl_classes!(Transform: FerroObjectImpl);
34
35impl TransformImpl for Transform {
36    fn value(_this: &Self) -> Matrix {
37        panic!("Transform is abstract: 'value' must be implemented by the deriving class")
38    }
39}
40
41impl Transform {
42    /// Creates the class data; see [`FerroObject::construct`].
43    pub fn construct() -> Self {
44        Self { base: Animatable::construct(), changed: HandlerList::new(), resource: CompositorResourceHolder::new() }
45    }
46
47    /// Parses a matrix string (`m11,m12,m21,m22,m31,m32` or a transform
48    /// operation list) to a transform.
49    pub fn parse(s: &str) -> Result<Ref<Transform>, FormatError> {
50        Ok(MatrixTransform::with_matrix(Matrix::parse(s)?).upcast())
51    }
52
53    /// Subscribes to changes of the transform. Disposing the returned handle
54    /// unsubscribes.
55    pub fn changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
56        let token = self.changed.add(Rc::new(handler));
57        let object: &FerroObject = self.upcast();
58        let weak = object.to_weak();
59        Disposable::create(move || {
60            if let Some(this) = weak.upgrade() {
61                if let Some(this) = this.downcast_ref::<Transform>() {
62                    this.changed.remove(token);
63                }
64            }
65        })
66    }
67
68    /// Raises the changed notification.
69    pub fn raise_changed(&self) {
70        if self.changed.is_empty() {
71            return;
72        }
73        for (_, handler) in self.changed.snapshot().iter() {
74            handler();
75        }
76    }
77
78    /// The object as something a compositor serializes: the adapter keeps
79    /// the object alive while it is queued.
80    pub(crate) fn as_compositor_serializable(&self) -> Rc<dyn ICompositorSerializable> {
81        Rc::new(RefAdapter(self.to_ref()))
82    }
83
84    /// Converts the transform to an immutable transform.
85    pub fn to_immutable(&self) -> ImmutableTransform {
86        ImmutableTransform::new(self.value())
87    }
88}
89
90impl ICompositionRenderResource for Transform {
91    fn add_ref_on_compositor(&self, c: &Rc<Compositor>) {
92        let owner = self.as_compositor_serializable();
93        self.resource.create_or_add_ref(c, Some(owner), |cc| {
94            cc.create_server_object(|server, _| ServerCompositionSimpleTransform::new(server) as Rc<dyn IServerObject>)
95        });
96    }
97
98    fn release_on_compositor(&self, c: &Rc<Compositor>) {
99        self.resource.release(c);
100    }
101
102    fn get_for_compositor(&self, c: &Compositor) -> ServerObjectId {
103        self.resource.get_for_compositor(c)
104    }
105}
106
107impl ICompositorSerializable for RefAdapter<Transform> {
108    fn try_get_server(&self, c: &Compositor) -> Option<ServerObjectId> {
109        self.0.resource.try_get_for_compositor(c)
110    }
111
112    fn serialization_key(&self) -> *const () {
113        RefAdapter::reference_id(self)
114    }
115
116    fn serialize_changes(&self, _c: &Compositor, writer: &mut BatchStreamWriter<'_>) {
117        ServerCompositionSimpleTransformProps::serialize_all_changes(writer, self.0.value());
118    }
119}
120
121/// Writes the transform's matrix.
122impl fmt::Display for Transform {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        fmt::Display::fmt(&self.value(), f)
125    }
126}