freya_core/
data.rs

1use std::{
2    borrow::Cow,
3    hash::Hash,
4    rc::Rc,
5};
6
7use torin::{
8    prelude::Area,
9    torin::Torin,
10};
11
12use crate::{
13    accessibility::{
14        dirty_nodes::AccessibilityDirtyNodes,
15        focusable::Focusable,
16        groups::AccessibilityGroups,
17        id::{
18            AccessibilityGenerator,
19            AccessibilityId,
20        },
21        tree::ACCESSIBILITY_ROOT_ID,
22    },
23    element::ElementExt,
24    layers::Layers,
25    node_id::NodeId,
26    prelude::AccessibilityFocusStrategy,
27    style::{
28        border::Border,
29        color::Color,
30        corner_radius::CornerRadius,
31        fill::Fill,
32        font_size::FontSize,
33        font_slant::FontSlant,
34        font_weight::FontWeight,
35        font_width::FontWidth,
36        scale::Scale,
37        shadow::Shadow,
38        text_align::TextAlign,
39        text_height::TextHeightBehavior,
40        text_overflow::TextOverflow,
41        text_shadow::TextShadow,
42    },
43};
44
45#[derive(Debug, Default, Clone, PartialEq)]
46pub struct LayoutData {
47    pub layout: torin::node::Node,
48}
49
50#[derive(Debug, Default, Clone, PartialEq)]
51pub struct EffectData {
52    pub overflow: Overflow,
53    pub rotation: Option<f32>,
54    pub scale: Option<Scale>,
55    pub opacity: Option<f32>,
56    pub scrollable: bool,
57}
58
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[derive(Debug, Default, Clone, PartialEq)]
61pub struct StyleState {
62    pub background: Fill,
63    pub corner_radius: CornerRadius,
64    pub borders: Vec<Border>,
65    pub shadows: Vec<Shadow>,
66}
67
68#[derive(Debug, Clone, PartialEq)]
69pub struct CursorStyleData {
70    pub color: Color,
71    pub highlight_color: Color,
72}
73
74impl Default for CursorStyleData {
75    fn default() -> Self {
76        Self {
77            color: Color::BLACK,
78            highlight_color: Color::from_rgb(87, 108, 188),
79        }
80    }
81}
82
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84#[derive(Debug, Clone, PartialEq, Hash)]
85pub struct TextStyleState {
86    pub font_size: FontSize,
87    pub color: Color,
88    pub text_align: TextAlign,
89    pub font_families: Vec<Cow<'static, str>>,
90    pub text_height: TextHeightBehavior,
91    pub text_overflow: TextOverflow,
92    pub text_shadows: Vec<TextShadow>,
93    pub font_slant: FontSlant,
94    pub font_weight: FontWeight,
95    pub font_width: FontWidth,
96}
97
98impl Default for TextStyleState {
99    fn default() -> Self {
100        Self {
101            font_size: FontSize::default(),
102            color: Color::BLACK,
103            text_align: TextAlign::default(),
104            font_families: Vec::new(),
105            text_height: TextHeightBehavior::default(),
106            text_overflow: TextOverflow::default(),
107            text_shadows: Vec::new(),
108            font_slant: FontSlant::default(),
109            font_weight: FontWeight::default(),
110            font_width: FontWidth::default(),
111        }
112    }
113}
114
115impl TextStyleState {
116    pub fn from_data(parent: &TextStyleState, data: &TextStyleData) -> Self {
117        let color = data.color.unwrap_or(parent.color);
118
119        let text_align = data.text_align.unwrap_or_default();
120        let text_height = data.text_height.unwrap_or_default();
121        let text_overflow = data.text_overflow.clone().unwrap_or_default();
122        let text_shadows = data.text_shadows.clone();
123
124        // Font values can be inherited
125        let font_size = data.font_size.unwrap_or(parent.font_size);
126        let font_slant = data.font_slant.unwrap_or(parent.font_slant);
127        let font_weight = data.font_weight.unwrap_or(parent.font_weight);
128        let font_width = data.font_width.unwrap_or(parent.font_width);
129        let mut font_families = data.font_families.clone();
130        font_families.extend_from_slice(&parent.font_families);
131
132        Self {
133            color,
134            text_align,
135            text_height,
136            text_overflow,
137            text_shadows,
138            font_size,
139            font_slant,
140            font_weight,
141            font_width,
142            font_families,
143        }
144    }
145
146    pub fn update(
147        &mut self,
148        node_id: NodeId,
149        parent_text_style: &Self,
150        element: &Rc<dyn ElementExt>,
151        layout: &mut Torin<NodeId>,
152    ) {
153        let text_style_data = element.text_style();
154
155        let text_style = Self::from_data(parent_text_style, &text_style_data);
156        let is_equal = *self == text_style;
157
158        *self = text_style;
159
160        if !is_equal {
161            // TODO: Only invalidate label and paragraphs
162            layout.invalidate(node_id);
163        }
164    }
165}
166
167#[derive(Debug, Clone, PartialEq, Default, Hash)]
168pub struct TextStyleData {
169    pub color: Option<Color>,
170    pub font_size: Option<FontSize>,
171    pub font_families: Vec<Cow<'static, str>>,
172    pub text_align: Option<TextAlign>,
173    pub text_height: Option<TextHeightBehavior>,
174    pub text_overflow: Option<TextOverflow>,
175    pub text_shadows: Vec<TextShadow>,
176    pub font_slant: Option<FontSlant>,
177    pub font_weight: Option<FontWeight>,
178    pub font_width: Option<FontWidth>,
179}
180
181#[derive(Debug, Default)]
182pub struct LayerState {
183    pub layer: i16,
184}
185
186impl LayerState {
187    pub fn create_for_root(node_id: NodeId, layers: &mut Layers) -> Self {
188        let layer = 0;
189
190        layers.insert_node_in_layer(node_id, layer);
191
192        Self { layer }
193    }
194
195    pub fn remove(self, node_id: NodeId, layers: &mut Layers) {
196        layers.remove_node_from_layer(&node_id, self.layer);
197    }
198
199    pub fn update(
200        &mut self,
201        parent_layer: &Self,
202        node_id: NodeId,
203        element: &Rc<dyn ElementExt>,
204        layers: &mut Layers,
205    ) {
206        let relative_layer = element.relative_layer();
207
208        // Old
209        layers.remove_node_from_layer(&node_id, self.layer);
210
211        // New
212        self.layer = parent_layer.layer + relative_layer + 1;
213        layers.insert_node_in_layer(node_id, self.layer);
214    }
215}
216
217#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
218pub enum Overflow {
219    #[default]
220    None,
221    Clip,
222}
223
224#[derive(PartialEq, Default, Debug, Clone)]
225pub struct EffectState {
226    pub overflow: Overflow,
227    pub clips: Rc<[NodeId]>,
228
229    pub rotations: Rc<[NodeId]>,
230    pub rotation: Option<f32>,
231
232    pub scales: Rc<[NodeId]>,
233    pub scale: Option<Scale>,
234
235    pub opacities: Rc<[f32]>,
236
237    pub scrollables: Rc<[NodeId]>,
238}
239
240impl EffectState {
241    pub fn update(
242        &mut self,
243        parent_node_id: NodeId,
244        parent_effect_state: &Self,
245        node_id: NodeId,
246        effect_data: Option<Cow<'_, EffectData>>,
247    ) {
248        *self = Self {
249            overflow: Overflow::default(),
250            ..parent_effect_state.clone()
251        };
252
253        if parent_effect_state.overflow == Overflow::Clip {
254            let mut clips = parent_effect_state.clips.to_vec();
255            clips.push(parent_node_id);
256            if self.clips.as_ref() != clips {
257                self.clips = Rc::from(clips);
258            }
259        }
260
261        if let Some(effect_data) = effect_data {
262            self.overflow = effect_data.overflow;
263
264            if let Some(rotation) = effect_data.rotation {
265                let mut rotations = parent_effect_state.rotations.to_vec();
266                rotations.push(node_id);
267                self.rotation = Some(rotation);
268                if self.rotations.as_ref() != rotations {
269                    self.rotations = Rc::from(rotations);
270                }
271            }
272
273            if let Some(scale) = effect_data.scale {
274                let mut scales = parent_effect_state.scales.to_vec();
275                scales.push(node_id);
276                self.scale = Some(scale);
277                if self.scales.as_ref() != scales {
278                    self.scales = Rc::from(scales);
279                }
280            }
281
282            if let Some(opacity) = effect_data.opacity {
283                let mut opacities = parent_effect_state.opacities.to_vec();
284                opacities.push(opacity);
285                if self.opacities.as_ref() != opacities {
286                    self.opacities = Rc::from(opacities);
287                }
288            }
289
290            if effect_data.scrollable {
291                let mut scrolls = parent_effect_state.scrollables.to_vec();
292                scrolls.push(node_id);
293                if self.scrollables.as_ref() != scrolls {
294                    self.scrollables = Rc::from(scrolls);
295                }
296            }
297        }
298    }
299
300    pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
301        // Skip elements that are completely out of any their parent's viewport
302        for viewport_id in self.clips.iter() {
303            let viewport = layout.get(viewport_id).unwrap().visible_area();
304            if !viewport.intersects(area) {
305                return false;
306            }
307        }
308        true
309    }
310}
311
312#[derive(PartialEq, Clone)]
313pub struct AccessibilityState {
314    pub a11y_id: AccessibilityId,
315    pub a11y_focusable: Focusable,
316    pub a11y_member_of: Option<AccessibilityId>,
317}
318
319impl AccessibilityState {
320    pub fn create(
321        node_id: NodeId,
322        element: &Rc<dyn ElementExt>,
323        accessibility_diff: &mut AccessibilityDirtyNodes,
324        accessibility_generator: &AccessibilityGenerator,
325        accessibility_groups: &mut AccessibilityGroups,
326    ) -> Self {
327        let data = element.accessibility();
328
329        let a11y_id = if node_id == NodeId::ROOT {
330            ACCESSIBILITY_ROOT_ID
331        } else {
332            data.a11y_id
333                .unwrap_or_else(|| AccessibilityId(accessibility_generator.new_id()))
334        };
335
336        accessibility_diff.add_or_update(node_id);
337
338        if let Some(member_of) = data.builder.member_of() {
339            let group = accessibility_groups.entry(member_of).or_default();
340            // This is not perfect as it assumes that order of creation is the same as the UI order
341            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
342            // So for no we just push to the end of the vector
343            group.push(a11y_id);
344        }
345
346        if data.a11y_auto_focus {
347            accessibility_diff.request_focus(AccessibilityFocusStrategy::Node(a11y_id));
348        }
349
350        Self {
351            a11y_id,
352            a11y_focusable: data.a11y_focusable.clone(),
353            a11y_member_of: data.builder.member_of(),
354        }
355    }
356
357    pub fn remove(
358        self,
359        node_id: NodeId,
360        parent_id: NodeId,
361        accessibility_diff: &mut AccessibilityDirtyNodes,
362        accessibility_groups: &mut AccessibilityGroups,
363    ) {
364        accessibility_diff.remove(node_id, parent_id);
365
366        if let Some(member_of) = self.a11y_member_of {
367            let group = accessibility_groups.get_mut(&member_of).unwrap();
368            group.retain(|id| *id != self.a11y_id);
369        }
370    }
371
372    pub fn update(
373        &mut self,
374        node_id: NodeId,
375        element: &Rc<dyn ElementExt>,
376        accessibility_diff: &mut AccessibilityDirtyNodes,
377        accessibility_groups: &mut AccessibilityGroups,
378    ) {
379        let data = element.accessibility();
380
381        if let Some(member_of) = self.a11y_member_of
382            && self.a11y_member_of != data.builder.member_of()
383        {
384            let group = accessibility_groups.get_mut(&member_of).unwrap();
385            group.retain(|id| *id != self.a11y_id);
386        }
387
388        if let Some(a11y_id) = data.a11y_id
389            && self.a11y_id != a11y_id
390        {
391            accessibility_diff.add_or_update(node_id);
392            self.a11y_id = a11y_id;
393        }
394
395        if let Some(member_of) = data.builder.member_of() {
396            let group = accessibility_groups.entry(member_of).or_default();
397            // This is not perfect as it assumes that order of creation is the same as the UI order
398            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
399            // So for no we just push to the end of the vector
400            group.push(self.a11y_id);
401
402            self.a11y_member_of = Some(member_of);
403        }
404
405        self.a11y_focusable = data.a11y_focusable.clone();
406    }
407}
408
409#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
410#[derive(Debug, Default, Clone, PartialEq)]
411pub struct AccessibilityData {
412    pub a11y_id: Option<AccessibilityId>,
413    pub a11y_auto_focus: bool,
414    pub a11y_focusable: Focusable,
415    pub builder: accesskit::Node,
416}