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