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
167        let text_align = data.text_align.unwrap_or_default();
168        let text_height = data.text_height.unwrap_or_default();
169        let text_overflow = data.text_overflow.clone().unwrap_or_default();
170        let text_shadows = data.text_shadows.clone();
171        let text_decoration = data.text_decoration.unwrap_or_default();
172
173        // Font values can be inherited
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        Self {
185            color,
186            text_align,
187            text_height,
188            text_overflow,
189            text_shadows,
190            text_decoration,
191            font_size,
192            font_slant,
193            font_weight,
194            font_width,
195            letter_spacing,
196            font_families,
197            font_features,
198        }
199    }
200
201    pub fn update(
202        &mut self,
203        node_id: NodeId,
204        parent_text_style: &Self,
205        element: &Rc<dyn ElementExt>,
206        layout: &mut Torin<NodeId>,
207    ) -> bool {
208        let text_style_data = element.text_style();
209
210        let text_style = Self::from_data(parent_text_style, &text_style_data);
211        let is_equal = *self == text_style;
212
213        *self = text_style;
214
215        if !is_equal {
216            // TODO: Only invalidate label and paragraphs
217            layout.invalidate(node_id);
218        }
219
220        !is_equal
221    }
222}
223
224#[derive(Debug, Clone, PartialEq, Default, Hash)]
225pub struct TextStyleData {
226    pub color: Option<Fill>,
227    pub font_size: Option<FontSize>,
228    pub font_families: Vec<Cow<'static, str>>,
229    pub text_align: Option<TextAlign>,
230    pub text_height: Option<TextHeightBehavior>,
231    pub text_overflow: Option<TextOverflow>,
232    pub text_shadows: Vec<TextShadow>,
233    pub text_decoration: Option<TextDecoration>,
234    pub font_slant: Option<FontSlant>,
235    pub font_weight: Option<FontWeight>,
236    pub font_width: Option<FontWidth>,
237    pub letter_spacing: Option<LetterSpacing>,
238    pub font_features: Vec<FontFeature>,
239}
240
241#[derive(Debug, Default)]
242pub struct LayerState {
243    pub layer: i16,
244}
245
246impl LayerState {
247    pub fn create_for_root(node_id: NodeId, layers: &mut Layers) -> Self {
248        let layer = 0;
249
250        layers.insert_node_in_layer(node_id, layer);
251
252        Self { layer }
253    }
254
255    pub fn remove(self, node_id: NodeId, layers: &mut Layers) {
256        layers.remove_node_from_layer(&node_id, self.layer);
257    }
258
259    pub fn update(
260        &mut self,
261        parent_layer: &Self,
262        node_id: NodeId,
263        element: &Rc<dyn ElementExt>,
264        layers: &mut Layers,
265    ) {
266        let relative_layer = element.layer();
267
268        // Old
269        layers.remove_node_from_layer(&node_id, self.layer);
270
271        // New
272        self.layer = match relative_layer {
273            Layer::Relative(relative_layer) => parent_layer
274                .layer
275                .saturating_add(relative_layer)
276                .saturating_add(1),
277            Layer::Overlay => parent_layer.layer.saturating_add(i16::MAX / 16),
278            Layer::OverlayLevel(overlay_level) => {
279                (overlay_level.max(1) as i16).saturating_mul(i16::MAX / 16)
280            }
281        };
282        layers.insert_node_in_layer(node_id, self.layer);
283    }
284}
285
286/// Whether content overflowing an element's bounds is shown or clipped.
287#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
288pub enum Overflow {
289    /// Let children paint outside the element's bounds. This is the default.
290    #[default]
291    None,
292    /// Clip children to the element's bounds.
293    Clip,
294}
295
296/// Whether an element (and its descendants) responds to pointer events.
297///
298/// Converts from a `bool`, where `true` is [`Interactive::Yes`].
299#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
300pub enum Interactive {
301    /// The element receives pointer events. This is the default.
302    #[default]
303    Yes,
304    /// The element ignores pointer events, letting them pass through.
305    No,
306}
307
308impl From<bool> for Interactive {
309    fn from(value: bool) -> Self {
310        match value {
311            true => Interactive::Yes,
312            false => Interactive::No,
313        }
314    }
315}
316
317#[derive(PartialEq, Default, Debug, Clone)]
318pub struct EffectState {
319    pub overflow: Overflow,
320    pub clips: Rc<[NodeId]>,
321
322    pub rotations: Rc<[NodeId]>,
323    pub rotation: Option<f32>,
324
325    pub scales: Rc<[NodeId]>,
326    pub scale: Option<Scale>,
327
328    pub transform_origin: TransformOrigin,
329
330    pub opacities: Rc<[f32]>,
331
332    pub blur: Option<f32>,
333
334    pub scrollables: Rc<[NodeId]>,
335
336    pub interactive: Interactive,
337}
338
339impl EffectState {
340    pub fn update(
341        &mut self,
342        parent_node_id: NodeId,
343        parent_effect_state: &Self,
344        node_id: NodeId,
345        effect_data: Option<Cow<'_, EffectData>>,
346        layer: Layer,
347    ) {
348        *self = Self {
349            overflow: Overflow::default(),
350            blur: None,
351            rotation: None,
352            scale: None,
353            transform_origin: TransformOrigin::default(),
354            ..parent_effect_state.clone()
355        };
356
357        match layer {
358            Layer::Overlay | Layer::OverlayLevel(_) => {
359                self.clips = Rc::default();
360            }
361            Layer::Relative(_) if parent_effect_state.overflow == Overflow::Clip => {
362                let mut clips = parent_effect_state.clips.to_vec();
363                clips.push(parent_node_id);
364                if self.clips.as_ref() != clips {
365                    self.clips = Rc::from(clips);
366                }
367            }
368            _ => {}
369        }
370
371        if let Some(effect_data) = effect_data {
372            self.overflow = effect_data.overflow;
373            self.blur = effect_data.blur;
374            self.transform_origin = effect_data.transform_origin;
375
376            if let Some(rotation) = effect_data.rotation {
377                let mut rotations = parent_effect_state.rotations.to_vec();
378                rotations.push(node_id);
379                self.rotation = Some(rotation);
380                if self.rotations.as_ref() != rotations {
381                    self.rotations = Rc::from(rotations);
382                }
383            }
384
385            if let Some(scale) = effect_data.scale {
386                let mut scales = parent_effect_state.scales.to_vec();
387                scales.push(node_id);
388                self.scale = Some(scale);
389                if self.scales.as_ref() != scales {
390                    self.scales = Rc::from(scales);
391                }
392            }
393
394            if let Some(opacity) = effect_data.opacity {
395                let mut opacities = parent_effect_state.opacities.to_vec();
396                opacities.push(opacity);
397                if self.opacities.as_ref() != opacities {
398                    self.opacities = Rc::from(opacities);
399                }
400            }
401
402            if effect_data.scrollable {
403                let mut scrolls = parent_effect_state.scrollables.to_vec();
404                scrolls.push(node_id);
405                if self.scrollables.as_ref() != scrolls {
406                    self.scrollables = Rc::from(scrolls);
407                }
408            }
409
410            if effect_data.interactive == Interactive::No {
411                self.interactive = Interactive::No;
412            }
413        }
414    }
415
416    pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
417        // Skip elements that are completely out of any their parent's viewport
418        for viewport_id in self.clips.iter() {
419            let viewport = layout.get(viewport_id).unwrap().visible_area();
420            if !viewport.intersects(area) {
421                return false;
422            }
423        }
424        true
425    }
426}
427
428#[derive(PartialEq, Clone)]
429pub struct AccessibilityState {
430    pub a11y_id: AccessibilityId,
431    pub a11y_focusable: Focusable,
432    pub a11y_member_of: Option<AccessibilityId>,
433    a11y_intrinsic_id: AccessibilityId,
434}
435
436impl AccessibilityState {
437    pub fn create(
438        node_id: NodeId,
439        element: &Rc<dyn ElementExt>,
440        accessibility_diff: &mut AccessibilityDirtyNodes,
441        accessibility_generator: &AccessibilityGenerator,
442        accessibility_groups: &mut AccessibilityGroups,
443    ) -> Self {
444        let data = element.accessibility();
445
446        let a11y_intrinsic_id = if node_id == NodeId::ROOT {
447            ACCESSIBILITY_ROOT_ID
448        } else {
449            AccessibilityId(accessibility_generator.new_id())
450        };
451        let a11y_id = data
452            .a11y_id
453            .filter(|_| node_id != NodeId::ROOT)
454            .unwrap_or(a11y_intrinsic_id);
455
456        accessibility_diff.add_or_update(node_id);
457
458        if let Some(member_of) = data.builder.member_of() {
459            let group = accessibility_groups.entry(member_of).or_default();
460            // This is not perfect as it assumes that order of creation is the same as the UI order
461            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
462            // So for no we just push to the end of the vector
463            group.push(a11y_id);
464        }
465
466        if data.a11y_auto_focus {
467            accessibility_diff.request_auto_focus(AccessibilityFocusStrategy::Node(a11y_id));
468        }
469
470        Self {
471            a11y_id,
472            a11y_focusable: data.a11y_focusable.clone(),
473            a11y_member_of: data.builder.member_of(),
474            a11y_intrinsic_id,
475        }
476    }
477
478    pub fn remove(
479        self,
480        node_id: NodeId,
481        parent_id: NodeId,
482        accessibility_diff: &mut AccessibilityDirtyNodes,
483        accessibility_groups: &mut AccessibilityGroups,
484    ) {
485        accessibility_diff.remove(node_id, parent_id);
486
487        if let Some(member_of) = self.a11y_member_of {
488            let group = accessibility_groups.get_mut(&member_of).unwrap();
489            group.retain(|id| *id != self.a11y_id);
490        }
491    }
492
493    pub fn update(
494        &mut self,
495        node_id: NodeId,
496        element: &Rc<dyn ElementExt>,
497        accessibility_diff: &mut AccessibilityDirtyNodes,
498        accessibility_groups: &mut AccessibilityGroups,
499    ) {
500        let data = element.accessibility();
501
502        if let Some(member_of) = self.a11y_member_of
503            && self.a11y_member_of != data.builder.member_of()
504        {
505            let group = accessibility_groups.get_mut(&member_of).unwrap();
506            group.retain(|id| *id != self.a11y_id);
507        }
508
509        let a11y_id = data.a11y_id.unwrap_or(self.a11y_intrinsic_id);
510        if self.a11y_id != a11y_id {
511            accessibility_diff.add_or_update(node_id);
512            self.a11y_id = a11y_id;
513        }
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}