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