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