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