freya_core/states/
transform.rs1use std::sync::{
2 Arc,
3 Mutex,
4};
5
6use freya_native_core::{
7 exports::shipyard::Component,
8 node_ref::NodeView,
9 prelude::{
10 AttributeMaskBuilder,
11 AttributeName,
12 Dependancy,
13 NodeMaskBuilder,
14 State,
15 },
16 NodeId,
17 SendAnyMap,
18};
19use freya_native_core_macro::partial_derive_state;
20
21use crate::{
22 custom_attributes::CustomAttributeValues,
23 dom::CompositorDirtyNodes,
24 parsing::{
25 ParseAttribute,
26 ParseError,
27 },
28};
29
30#[derive(Default, Clone, Debug, Component, PartialEq)]
31pub struct TransformState {
32 pub node_id: NodeId,
33 pub opacities: Vec<f32>,
34 pub rotations: Vec<(NodeId, f32)>,
35 pub scales: Vec<(NodeId, f32, f32)>,
36}
37
38impl ParseAttribute for TransformState {
39 fn parse_attribute(
40 &mut self,
41 attr: freya_native_core::prelude::OwnedAttributeView<CustomAttributeValues>,
42 ) -> Result<(), ParseError> {
43 #[allow(clippy::single_match)]
44 match attr.attribute {
45 AttributeName::Rotate => {
46 if let Some(value) = attr.value.as_text() {
47 if value.ends_with("deg") {
48 let rotation = value
49 .replacen("deg", "", 1)
50 .parse::<f32>()
51 .map_err(|_| ParseError)?;
52 self.rotations.push((self.node_id, rotation));
53 }
54 }
55 }
56 AttributeName::Opacity => {
57 if let Some(value) = attr.value.as_text() {
58 let opacity = value.parse::<f32>().map_err(|_| ParseError)?;
59 self.opacities.push(opacity)
60 }
61 }
62 AttributeName::Scale => {
63 if let Some(value) = attr.value.as_text() {
64 let (scale_x, scale_y) = if !value.trim().contains(' ') {
65 let scale = value.parse::<f32>().map_err(|_| ParseError)?;
66 (scale, scale)
67 } else {
68 let Some((scale_x, scale_y)) = value.split_once(' ') else {
69 return Err(ParseError);
70 };
71 let scale_x = scale_x.parse::<f32>().map_err(|_| ParseError)?;
72 let scale_y = scale_y.parse::<f32>().map_err(|_| ParseError)?;
73 (scale_x, scale_y)
74 };
75 self.scales
76 .push((self.node_id, scale_x.max(0.), scale_y.max(0.)))
77 }
78 }
79 _ => {}
80 }
81
82 Ok(())
83 }
84}
85
86#[partial_derive_state]
87impl State<CustomAttributeValues> for TransformState {
88 type ParentDependencies = (Self,);
89
90 type ChildDependencies = ();
91
92 type NodeDependencies = ();
93
94 const NODE_MASK: NodeMaskBuilder<'static> =
95 NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&[
96 AttributeName::Rotate,
97 AttributeName::Opacity,
98 AttributeName::Scale,
99 AttributeName::AspectRatio,
100 AttributeName::ImageCover,
101 ]));
102
103 fn update<'a>(
104 &mut self,
105 node_view: NodeView<CustomAttributeValues>,
106 _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
107 parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
108 _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
109 context: &SendAnyMap,
110 ) -> bool {
111 let root_id = context.get::<NodeId>().unwrap();
112 let compositor_dirty_nodes = context.get::<Arc<Mutex<CompositorDirtyNodes>>>().unwrap();
113 let inherited_transform = parent.map(|(p,)| p.clone()).unwrap_or_default();
114
115 let mut transform_state = TransformState {
116 node_id: node_view.node_id(),
117 ..inherited_transform
118 };
119
120 if let Some(attributes) = node_view.attributes() {
121 for attr in attributes {
122 transform_state.parse_safe(attr);
123 }
124 }
125
126 let changed = transform_state != *self;
127
128 let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
129
130 if changed && !is_orphan {
131 compositor_dirty_nodes
132 .lock()
133 .unwrap()
134 .invalidate(node_view.node_id());
135 }
136
137 *self = transform_state;
138 changed
139 }
140}