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