freya_core/states/
style.rs

1use std::sync::{
2    Arc,
3    Mutex,
4};
5
6use freya_native_core::{
7    attributes::AttributeName,
8    exports::shipyard::Component,
9    node_ref::NodeView,
10    prelude::{
11        AttributeMaskBuilder,
12        Dependancy,
13        NodeMaskBuilder,
14        State,
15    },
16    SendAnyMap,
17};
18use freya_native_core_macro::partial_derive_state;
19
20use crate::{
21    custom_attributes::CustomAttributeValues,
22    dom::CompositorDirtyNodes,
23    parsing::{
24        ExtSplit,
25        Parse,
26        ParseAttribute,
27        ParseError,
28    },
29    values::{
30        parse_alpha,
31        Border,
32        CornerRadius,
33        Fill,
34        OverflowMode,
35        Shadow,
36    },
37};
38
39#[derive(Default, Debug, Clone, PartialEq, Component)]
40pub struct StyleState {
41    pub background: Fill,
42    pub background_opacity: Option<u8>,
43    pub borders: Arc<[Border]>,
44    pub shadows: Arc<[Shadow]>,
45    pub corner_radius: CornerRadius,
46    pub overflow: OverflowMode,
47}
48
49impl ParseAttribute for StyleState {
50    fn parse_attribute(
51        &mut self,
52        attr: freya_native_core::prelude::OwnedAttributeView<CustomAttributeValues>,
53    ) -> Result<(), ParseError> {
54        match attr.attribute {
55            AttributeName::Background => {
56                if let Some(value) = attr.value.as_text() {
57                    if value == "none" {
58                        return Ok(());
59                    }
60                    self.background = Fill::parse(value)?;
61                }
62            }
63            AttributeName::BackgroundOpacity => {
64                if let Some(value) = attr.value.as_text() {
65                    if value == "none" {
66                        return Ok(());
67                    }
68                    self.background_opacity = Some(parse_alpha(value)?);
69                }
70            }
71            AttributeName::Border => {
72                if let Some(value) = attr.value.as_text() {
73                    self.borders = value
74                        .split_excluding_group(',', '(', ')')
75                        .map(|chunk| Border::parse(chunk).unwrap_or_default())
76                        .collect();
77                }
78            }
79            AttributeName::Shadow => {
80                if let Some(value) = attr.value.as_text() {
81                    self.shadows = value
82                        .split_excluding_group(',', '(', ')')
83                        .map(|chunk| Shadow::parse(chunk).unwrap_or_default())
84                        .collect();
85                }
86            }
87            AttributeName::CornerRadius => {
88                if let Some(value) = attr.value.as_text() {
89                    let mut radius = CornerRadius::parse(value)?;
90                    radius.smoothing = self.corner_radius.smoothing;
91                    self.corner_radius = radius;
92                }
93            }
94            AttributeName::CornerSmoothing => {
95                if let Some(value) = attr.value.as_text() {
96                    if value.ends_with('%') {
97                        let smoothing = value
98                            .replacen('%', "", 1)
99                            .parse::<f32>()
100                            .map_err(|_| ParseError)?;
101                        self.corner_radius.smoothing = (smoothing / 100.0).clamp(0.0, 1.0);
102                    }
103                }
104            }
105
106            AttributeName::Overflow => {
107                if let Some(value) = attr.value.as_text() {
108                    self.overflow = OverflowMode::parse(value)?;
109                }
110            }
111
112            _ => {}
113        }
114
115        Ok(())
116    }
117}
118
119#[partial_derive_state]
120impl State<CustomAttributeValues> for StyleState {
121    type ParentDependencies = ();
122
123    type ChildDependencies = ();
124
125    type NodeDependencies = ();
126
127    const NODE_MASK: NodeMaskBuilder<'static> =
128        NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&[
129            AttributeName::Background,
130            AttributeName::BackgroundOpacity,
131            AttributeName::Layer,
132            AttributeName::Border,
133            AttributeName::Shadow,
134            AttributeName::CornerRadius,
135            AttributeName::CornerSmoothing,
136            AttributeName::Sampling,
137            AttributeName::ImageData,
138            AttributeName::Overflow,
139            AttributeName::ImageCacheKey,
140        ]));
141
142    fn update<'a>(
143        &mut self,
144        node_view: NodeView<CustomAttributeValues>,
145        _node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
146        _parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
147        _children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
148        context: &SendAnyMap,
149    ) -> bool {
150        let mut style = StyleState::default();
151
152        if let Some(attributes) = node_view.attributes() {
153            for attr in attributes {
154                style.parse_safe(attr)
155            }
156        }
157
158        if let Some(background_opacity) = style.background_opacity {
159            style.background.set_a(background_opacity);
160        }
161
162        let changed = &style != self;
163
164        if changed {
165            let compositor_dirty_nodes = context.get::<Arc<Mutex<CompositorDirtyNodes>>>().unwrap();
166            compositor_dirty_nodes
167                .lock()
168                .unwrap()
169                .invalidate(node_view.node_id());
170        }
171
172        *self = style;
173        changed
174    }
175}