freya_core/
data.rs

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