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: Fill,
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: Fill::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.as_ref().unwrap_or(&parent.color).clone();
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<Fill>,
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.max(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            if effect_data.interactive == Interactive::No {
379                self.interactive = Interactive::No;
380            }
381        }
382    }
383
384    pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
385        // Skip elements that are completely out of any their parent's viewport
386        for viewport_id in self.clips.iter() {
387            let viewport = layout.get(viewport_id).unwrap().visible_area();
388            if !viewport.intersects(area) {
389                return false;
390            }
391        }
392        true
393    }
394}
395
396#[derive(PartialEq, Clone)]
397pub struct AccessibilityState {
398    pub a11y_id: AccessibilityId,
399    pub a11y_focusable: Focusable,
400    pub a11y_member_of: Option<AccessibilityId>,
401}
402
403impl AccessibilityState {
404    pub fn create(
405        node_id: NodeId,
406        element: &Rc<dyn ElementExt>,
407        accessibility_diff: &mut AccessibilityDirtyNodes,
408        accessibility_generator: &AccessibilityGenerator,
409        accessibility_groups: &mut AccessibilityGroups,
410    ) -> Self {
411        let data = element.accessibility();
412
413        let a11y_id = if node_id == NodeId::ROOT {
414            ACCESSIBILITY_ROOT_ID
415        } else {
416            data.a11y_id
417                .unwrap_or_else(|| AccessibilityId(accessibility_generator.new_id()))
418        };
419
420        accessibility_diff.add_or_update(node_id);
421
422        if let Some(member_of) = data.builder.member_of() {
423            let group = accessibility_groups.entry(member_of).or_default();
424            // This is not perfect as it assumes that order of creation is the same as the UI order
425            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
426            // So for no we just push to the end of the vector
427            group.push(a11y_id);
428        }
429
430        if data.a11y_auto_focus {
431            accessibility_diff.request_focus(AccessibilityFocusStrategy::Node(a11y_id));
432        }
433
434        Self {
435            a11y_id,
436            a11y_focusable: data.a11y_focusable.clone(),
437            a11y_member_of: data.builder.member_of(),
438        }
439    }
440
441    pub fn remove(
442        self,
443        node_id: NodeId,
444        parent_id: NodeId,
445        accessibility_diff: &mut AccessibilityDirtyNodes,
446        accessibility_groups: &mut AccessibilityGroups,
447    ) {
448        accessibility_diff.remove(node_id, parent_id);
449
450        if let Some(member_of) = self.a11y_member_of {
451            let group = accessibility_groups.get_mut(&member_of).unwrap();
452            group.retain(|id| *id != self.a11y_id);
453        }
454    }
455
456    pub fn update(
457        &mut self,
458        node_id: NodeId,
459        element: &Rc<dyn ElementExt>,
460        accessibility_diff: &mut AccessibilityDirtyNodes,
461        accessibility_groups: &mut AccessibilityGroups,
462    ) {
463        let data = element.accessibility();
464
465        if let Some(member_of) = self.a11y_member_of
466            && self.a11y_member_of != data.builder.member_of()
467        {
468            let group = accessibility_groups.get_mut(&member_of).unwrap();
469            group.retain(|id| *id != self.a11y_id);
470        }
471
472        if let Some(a11y_id) = data.a11y_id
473            && self.a11y_id != a11y_id
474        {
475            accessibility_diff.add_or_update(node_id);
476            self.a11y_id = a11y_id;
477        }
478
479        if let Some(member_of) = data.builder.member_of() {
480            let group = accessibility_groups.entry(member_of).or_default();
481            // This is not perfect as it assumes that order of creation is the same as the UI order
482            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
483            // So for no we just push to the end of the vector
484            group.push(self.a11y_id);
485
486            self.a11y_member_of = Some(member_of);
487        }
488
489        self.a11y_focusable = data.a11y_focusable.clone();
490    }
491}
492
493#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
494#[derive(Debug, Default, Clone, PartialEq)]
495pub struct AccessibilityData {
496    pub a11y_id: Option<AccessibilityId>,
497    pub a11y_auto_focus: bool,
498    pub a11y_focusable: Focusable,
499    pub builder: accesskit::Node,
500}