Skip to main content

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