freya_core/states/
layout.rs1use 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}