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
248                .layer
249                .saturating_add(relative_layer)
250                .saturating_add(1),
251            Layer::Overlay => parent_layer.layer.saturating_add(i16::MAX / 16),
252            Layer::RelativeOverlay(relative_layer) => {
253                (relative_layer.min(1) as i16).saturating_mul(i16::MAX / 16)
254            }
255        };
256        layers.insert_node_in_layer(node_id, self.layer);
257    }
258}
259
260#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
261pub enum Overflow {
262    #[default]
263    None,
264    Clip,
265}
266
267#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
268pub enum Interactive {
269    #[default]
270    Yes,
271    No,
272}
273
274impl From<bool> for Interactive {
275    fn from(value: bool) -> Self {
276        match value {
277            true => Interactive::Yes,
278            false => Interactive::No,
279        }
280    }
281}
282
283#[derive(PartialEq, Default, Debug, Clone)]
284pub struct EffectState {
285    pub overflow: Overflow,
286    pub clips: Rc<[NodeId]>,
287
288    pub rotations: Rc<[NodeId]>,
289    pub rotation: Option<f32>,
290
291    pub scales: Rc<[NodeId]>,
292    pub scale: Option<Scale>,
293
294    pub opacities: Rc<[f32]>,
295
296    pub blur: Option<f32>,
297
298    pub scrollables: Rc<[NodeId]>,
299
300    pub interactive: Interactive,
301}
302
303impl EffectState {
304    pub fn update(
305        &mut self,
306        parent_node_id: NodeId,
307        parent_effect_state: &Self,
308        node_id: NodeId,
309        effect_data: Option<Cow<'_, EffectData>>,
310        layer: Layer,
311    ) {
312        *self = Self {
313            overflow: Overflow::default(),
314            blur: None,
315            rotation: None,
316            scale: None,
317            ..parent_effect_state.clone()
318        };
319
320        match layer {
321            Layer::Overlay => {
322                self.clips = Rc::default();
323            }
324            Layer::Relative(_) if parent_effect_state.overflow == Overflow::Clip => {
325                let mut clips = parent_effect_state.clips.to_vec();
326                clips.push(parent_node_id);
327                if self.clips.as_ref() != clips {
328                    self.clips = Rc::from(clips);
329                }
330            }
331            _ => {}
332        }
333
334        if let Some(effect_data) = effect_data {
335            self.overflow = effect_data.overflow;
336            self.blur = effect_data.blur;
337
338            if let Some(rotation) = effect_data.rotation {
339                let mut rotations = parent_effect_state.rotations.to_vec();
340                rotations.push(node_id);
341                self.rotation = Some(rotation);
342                if self.rotations.as_ref() != rotations {
343                    self.rotations = Rc::from(rotations);
344                }
345            }
346
347            if let Some(scale) = effect_data.scale {
348                let mut scales = parent_effect_state.scales.to_vec();
349                scales.push(node_id);
350                self.scale = Some(scale);
351                if self.scales.as_ref() != scales {
352                    self.scales = Rc::from(scales);
353                }
354            }
355
356            if let Some(opacity) = effect_data.opacity {
357                let mut opacities = parent_effect_state.opacities.to_vec();
358                opacities.push(opacity);
359                if self.opacities.as_ref() != opacities {
360                    self.opacities = Rc::from(opacities);
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}