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