freya_core/states/
layout.rs

1use std::sync::{
2    Arc,
3    Mutex,
4};
5
6use freya_native_core::{
7    attributes::AttributeName,
8    exports::shipyard::Component,
9    node::OwnedAttributeValue,
10    node_ref::NodeView,
11    prelude::{
12        AttributeMaskBuilder,
13        Dependancy,
14        NodeMaskBuilder,
15        OwnedAttributeView,
16        State,
17    },
18    NodeId,
19    SendAnyMap,
20};
21use freya_native_core_macro::partial_derive_state;
22use torin::prelude::*;
23
24use crate::{
25    custom_attributes::{
26        CustomAttributeValues,
27        NodeReference,
28    },
29    dom::CompositorDirtyNodes,
30    parsing::{
31        Parse,
32        ParseAttribute,
33        ParseError,
34    },
35};
36
37#[derive(Default, Clone, Debug, Component, PartialEq)]
38pub struct LayoutState {
39    pub width: Size,
40    pub height: Size,
41    pub minimum_width: Size,
42    pub minimum_height: Size,
43    pub maximum_height: Size,
44    pub maximum_width: Size,
45    pub visible_width: VisibleSize,
46    pub visible_height: VisibleSize,
47    pub padding: Gaps,
48    pub margin: Gaps,
49    pub direction: Direction,
50    pub offset_y: Length,
51    pub offset_x: Length,
52    pub main_alignment: Alignment,
53    pub cross_alignment: Alignment,
54    pub position: Position,
55    pub content: Content,
56    pub node_ref: Option<NodeReference>,
57    pub node_id: NodeId,
58    pub spacing: Length,
59}
60
61impl ParseAttribute for LayoutState {
62    fn parse_attribute(
63        &mut self,
64        attr: OwnedAttributeView<CustomAttributeValues>,
65    ) -> Result<(), ParseError> {
66        match attr.attribute {
67            AttributeName::Width => {
68                if let Some(value) = attr.value.as_text() {
69                    self.width = Size::parse(value)?;
70                }
71            }
72            AttributeName::Height => {
73                if let Some(value) = attr.value.as_text() {
74                    self.height = Size::parse(value)?;
75                }
76            }
77            AttributeName::MinHeight => {
78                if let Some(value) = attr.value.as_text() {
79                    self.minimum_height = Size::parse(value)?;
80                }
81            }
82            AttributeName::MinWidth => {
83                if let Some(value) = attr.value.as_text() {
84                    self.minimum_width = Size::parse(value)?;
85                }
86            }
87            AttributeName::MaxHeight => {
88                if let Some(value) = attr.value.as_text() {
89                    self.maximum_height = Size::parse(value)?;
90                }
91            }
92            AttributeName::MaxWidth => {
93                if let Some(value) = attr.value.as_text() {
94                    self.maximum_width = Size::parse(value)?;
95                }
96            }
97            AttributeName::VisibleWidth => {
98                if let Some(value) = attr.value.as_text() {
99                    self.visible_width = VisibleSize::parse(value)?;
100                }
101            }
102            AttributeName::VisibleHeight => {
103                if let Some(value) = attr.value.as_text() {
104                    self.visible_height = VisibleSize::parse(value)?;
105                }
106            }
107            AttributeName::Padding => {
108                if let Some(value) = attr.value.as_text() {
109                    self.padding = Gaps::parse(value)?;
110                }
111            }
112            AttributeName::Margin => {
113                if let Some(value) = attr.value.as_text() {
114                    self.margin = Gaps::parse(value)?;
115                }
116            }
117            AttributeName::Direction => {
118                if let Some(value) = attr.value.as_text() {
119                    self.direction = match value {
120                        "horizontal" => Direction::Horizontal,
121                        _ => Direction::Vertical,
122                    }
123                }
124            }
125            AttributeName::OffsetY => {
126                if let Some(value) = attr.value.as_text() {
127                    self.offset_y = Length::new(value.parse::<f32>().map_err(|_| ParseError)?);
128                }
129            }
130            AttributeName::OffsetX => {
131                if let Some(value) = attr.value.as_text() {
132                    self.offset_x = Length::new(value.parse::<f32>().map_err(|_| ParseError)?);
133                }
134            }
135            AttributeName::MainAlign => {
136                if let Some(value) = attr.value.as_text() {
137                    self.main_alignment = Alignment::parse(value)?;
138                }
139            }
140            AttributeName::CrossAlign => {
141                if let Some(value) = attr.value.as_text() {
142                    self.cross_alignment = Alignment::parse(value)?;
143                }
144            }
145            AttributeName::Position => {
146                if let Some(value) = attr.value.as_text() {
147                    self.position.swap_for(Position::parse(value)?);
148                }
149            }
150            AttributeName::PositionTop => {
151                if let Some(value) = attr.value.as_text() {
152                    self.position
153                        .set_top(value.parse::<f32>().map_err(|_| ParseError)?);
154                }
155            }
156            AttributeName::PositionRight => {
157                if let Some(value) = attr.value.as_text() {
158                    self.position
159                        .set_right(value.parse::<f32>().map_err(|_| ParseError)?);
160                }
161            }
162            AttributeName::PositionBottom => {
163                if let Some(value) = attr.value.as_text() {
164                    self.position
165                        .set_bottom(value.parse::<f32>().map_err(|_| ParseError)?);
166                }
167            }
168            AttributeName::PositionLeft => {
169                if let Some(value) = attr.value.as_text() {
170                    self.position
171                        .set_left(value.parse::<f32>().map_err(|_| ParseError)?);
172                }
173            }
174            AttributeName::Content => {
175                if let Some(value) = attr.value.as_text() {
176                    self.content = Content::parse(value)?;
177                }
178            }
179            AttributeName::Reference => {
180                if let OwnedAttributeValue::Custom(CustomAttributeValues::Reference(reference)) =
181                    attr.value
182                {
183                    self.node_ref = Some(reference.clone());
184                }
185            }
186            AttributeName::Spacing => {
187                if let Some(value) = attr.value.as_text() {
188                    self.spacing = Length::new(value.parse::<f32>().map_err(|_| ParseError)?);
189                }
190            }
191            _ => {}
192        }
193        Ok(())
194    }
195}
196
197#[partial_derive_state]
198impl State<CustomAttributeValues> for LayoutState {
199    type ParentDependencies = ();
200
201    type ChildDependencies = ();
202
203    type NodeDependencies = ();
204
205    const NODE_MASK: NodeMaskBuilder<'static> =
206        NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&[
207            AttributeName::Width,
208            AttributeName::Height,
209            AttributeName::MinWidth,
210            AttributeName::MinHeight,
211            AttributeName::MaxWidth,
212            AttributeName::MaxHeight,
213            AttributeName::VisibleWidth,
214            AttributeName::VisibleHeight,
215            AttributeName::Padding,
216            AttributeName::Direction,
217            AttributeName::OffsetX,
218            AttributeName::OffsetY,
219            AttributeName::MainAlign,
220            AttributeName::CrossAlign,
221            AttributeName::Reference,
222            AttributeName::Margin,
223            AttributeName::Position,
224            AttributeName::PositionTop,
225            AttributeName::PositionRight,
226            AttributeName::PositionBottom,
227            AttributeName::PositionLeft,
228            AttributeName::Content,
229            AttributeName::Spacing,
230        ]));
231
232    fn update<'a>(
233        &mut self,
234        node_view: NodeView<CustomAttributeValues>,
235        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
236        _parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
237        _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
238        context: &SendAnyMap,
239    ) -> bool {
240        let root_id = context.get::<NodeId>().unwrap();
241        let torin_layout = context.get::<Arc<Mutex<Torin<NodeId>>>>().unwrap();
242        let compositor_dirty_nodes = context.get::<Arc<Mutex<CompositorDirtyNodes>>>().unwrap();
243
244        let mut layout = LayoutState {
245            node_id: node_view.node_id(),
246            ..Default::default()
247        };
248
249        if let Some(attributes) = node_view.attributes() {
250            for attr in attributes {
251                layout.parse_safe(attr);
252            }
253        }
254
255        let changed = layout != *self;
256
257        let is_orphan = node_view.height() == 0 && node_view.node_id() != *root_id;
258
259        if changed && !is_orphan {
260            torin_layout.lock().unwrap().invalidate(node_view.node_id());
261            compositor_dirty_nodes
262                .lock()
263                .unwrap()
264                .invalidate(node_view.node_id());
265        }
266
267        *self = layout;
268        changed
269    }
270}