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#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
269pub enum Overflow {
270    #[default]
271    None,
272    Clip,
273}
274
275#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
276pub enum Interactive {
277    #[default]
278    Yes,
279    No,
280}
281
282impl From<bool> for Interactive {
283    fn from(value: bool) -> Self {
284        match value {
285            true => Interactive::Yes,
286            false => Interactive::No,
287        }
288    }
289}
290
291#[derive(PartialEq, Default, Debug, Clone)]
292pub struct EffectState {
293    pub overflow: Overflow,
294    pub clips: Rc<[NodeId]>,
295
296    pub rotations: Rc<[NodeId]>,
297    pub rotation: Option<f32>,
298
299    pub scales: Rc<[NodeId]>,
300    pub scale: Option<Scale>,
301
302    pub transform_origin: TransformOrigin,
303
304    pub opacities: Rc<[f32]>,
305
306    pub blur: Option<f32>,
307
308    pub scrollables: Rc<[NodeId]>,
309
310    pub interactive: Interactive,
311}
312
313impl EffectState {
314    pub fn update(
315        &mut self,
316        parent_node_id: NodeId,
317        parent_effect_state: &Self,
318        node_id: NodeId,
319        effect_data: Option<Cow<'_, EffectData>>,
320        layer: Layer,
321    ) {
322        *self = Self {
323            overflow: Overflow::default(),
324            blur: None,
325            rotation: None,
326            scale: None,
327            transform_origin: TransformOrigin::default(),
328            ..parent_effect_state.clone()
329        };
330
331        match layer {
332            Layer::Overlay => {
333                self.clips = Rc::default();
334            }
335            Layer::Relative(_) if parent_effect_state.overflow == Overflow::Clip => {
336                let mut clips = parent_effect_state.clips.to_vec();
337                clips.push(parent_node_id);
338                if self.clips.as_ref() != clips {
339                    self.clips = Rc::from(clips);
340                }
341            }
342            _ => {}
343        }
344
345        if let Some(effect_data) = effect_data {
346            self.overflow = effect_data.overflow;
347            self.blur = effect_data.blur;
348            self.transform_origin = effect_data.transform_origin;
349
350            if let Some(rotation) = effect_data.rotation {
351                let mut rotations = parent_effect_state.rotations.to_vec();
352                rotations.push(node_id);
353                self.rotation = Some(rotation);
354                if self.rotations.as_ref() != rotations {
355                    self.rotations = Rc::from(rotations);
356                }
357            }
358
359            if let Some(scale) = effect_data.scale {
360                let mut scales = parent_effect_state.scales.to_vec();
361                scales.push(node_id);
362                self.scale = Some(scale);
363                if self.scales.as_ref() != scales {
364                    self.scales = Rc::from(scales);
365                }
366            }
367
368            if let Some(opacity) = effect_data.opacity {
369                let mut opacities = parent_effect_state.opacities.to_vec();
370                opacities.push(opacity);
371                if self.opacities.as_ref() != opacities {
372                    self.opacities = Rc::from(opacities);
373                }
374            }
375
376            if effect_data.scrollable {
377                let mut scrolls = parent_effect_state.scrollables.to_vec();
378                scrolls.push(node_id);
379                if self.scrollables.as_ref() != scrolls {
380                    self.scrollables = Rc::from(scrolls);
381                }
382            }
383
384            if effect_data.interactive == Interactive::No {
385                self.interactive = Interactive::No;
386            }
387        }
388    }
389
390    pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
391        // Skip elements that are completely out of any their parent's viewport
392        for viewport_id in self.clips.iter() {
393            let viewport = layout.get(viewport_id).unwrap().visible_area();
394            if !viewport.intersects(area) {
395                return false;
396            }
397        }
398        true
399    }
400}
401
402#[derive(PartialEq, Clone)]
403pub struct AccessibilityState {
404    pub a11y_id: AccessibilityId,
405    pub a11y_focusable: Focusable,
406    pub a11y_member_of: Option<AccessibilityId>,
407}
408
409impl AccessibilityState {
410    pub fn create(
411        node_id: NodeId,
412        element: &Rc<dyn ElementExt>,
413        accessibility_diff: &mut AccessibilityDirtyNodes,
414        accessibility_generator: &AccessibilityGenerator,
415        accessibility_groups: &mut AccessibilityGroups,
416    ) -> Self {
417        let data = element.accessibility();
418
419        let a11y_id = if node_id == NodeId::ROOT {
420            ACCESSIBILITY_ROOT_ID
421        } else {
422            data.a11y_id
423                .unwrap_or_else(|| AccessibilityId(accessibility_generator.new_id()))
424        };
425
426        accessibility_diff.add_or_update(node_id);
427
428        if let Some(member_of) = data.builder.member_of() {
429            let group = accessibility_groups.entry(member_of).or_default();
430            // This is not perfect as it assumes that order of creation is the same as the UI order
431            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
432            // So for no we just push to the end of the vector
433            group.push(a11y_id);
434        }
435
436        if data.a11y_auto_focus {
437            accessibility_diff.request_focus(AccessibilityFocusStrategy::Node(a11y_id));
438        }
439
440        Self {
441            a11y_id,
442            a11y_focusable: data.a11y_focusable.clone(),
443            a11y_member_of: data.builder.member_of(),
444        }
445    }
446
447    pub fn remove(
448        self,
449        node_id: NodeId,
450        parent_id: NodeId,
451        accessibility_diff: &mut AccessibilityDirtyNodes,
452        accessibility_groups: &mut AccessibilityGroups,
453    ) {
454        accessibility_diff.remove(node_id, parent_id);
455
456        if let Some(member_of) = self.a11y_member_of {
457            let group = accessibility_groups.get_mut(&member_of).unwrap();
458            group.retain(|id| *id != self.a11y_id);
459        }
460    }
461
462    pub fn update(
463        &mut self,
464        node_id: NodeId,
465        element: &Rc<dyn ElementExt>,
466        accessibility_diff: &mut AccessibilityDirtyNodes,
467        accessibility_groups: &mut AccessibilityGroups,
468    ) {
469        let data = element.accessibility();
470
471        if let Some(member_of) = self.a11y_member_of
472            && self.a11y_member_of != data.builder.member_of()
473        {
474            let group = accessibility_groups.get_mut(&member_of).unwrap();
475            group.retain(|id| *id != self.a11y_id);
476        }
477
478        if let Some(a11y_id) = data.a11y_id
479            && self.a11y_id != a11y_id
480        {
481            accessibility_diff.add_or_update(node_id);
482            self.a11y_id = a11y_id;
483        }
484
485        if let Some(member_of) = data.builder.member_of() {
486            let group = accessibility_groups.entry(member_of).or_default();
487            // This is not perfect as it assumes that order of creation is the same as the UI order
488            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
489            // So for no we just push to the end of the vector
490            group.push(self.a11y_id);
491
492            self.a11y_member_of = Some(member_of);
493        }
494
495        self.a11y_focusable = data.a11y_focusable.clone();
496    }
497}
498
499#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
500#[derive(Debug, Default, Clone, PartialEq)]
501pub struct AccessibilityData {
502    pub a11y_id: Option<AccessibilityId>,
503    pub a11y_auto_focus: bool,
504    pub a11y_focusable: Focusable,
505    pub builder: accesskit::Node,
506}