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