freya_core/
data.rs

1use std::{
2    borrow::Cow,
3    hash::Hash,
4    rc::Rc,
5};
6
7use torin::{
8    prelude::Area,
9    torin::Torin,
10};
11
12use crate::{
13    accessibility::{
14        dirty_nodes::AccessibilityDirtyNodes,
15        focusable::Focusable,
16        groups::AccessibilityGroups,
17        id::{
18            AccessibilityGenerator,
19            AccessibilityId,
20        },
21        tree::ACCESSIBILITY_ROOT_ID,
22    },
23    element::ElementExt,
24    layers::{
25        Layer,
26        Layers,
27    },
28    node_id::NodeId,
29    prelude::AccessibilityFocusStrategy,
30    style::{
31        border::Border,
32        color::Color,
33        corner_radius::CornerRadius,
34        fill::Fill,
35        font_size::FontSize,
36        font_slant::FontSlant,
37        font_weight::FontWeight,
38        font_width::FontWidth,
39        scale::Scale,
40        shadow::Shadow,
41        text_align::TextAlign,
42        text_height::TextHeightBehavior,
43        text_overflow::TextOverflow,
44        text_shadow::TextShadow,
45    },
46};
47
48#[derive(Debug, Default, Clone, PartialEq)]
49pub struct LayoutData {
50    pub layout: torin::node::Node,
51}
52
53#[derive(Debug, Default, Clone, PartialEq)]
54pub struct EffectData {
55    pub overflow: Overflow,
56    pub rotation: Option<f32>,
57    pub scale: Option<Scale>,
58    pub opacity: Option<f32>,
59    pub scrollable: bool,
60    pub interactive: Interactive,
61}
62
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64#[derive(Debug, Default, Clone, PartialEq)]
65pub struct StyleState {
66    pub background: Fill,
67    pub corner_radius: CornerRadius,
68    pub borders: Vec<Border>,
69    pub shadows: Vec<Shadow>,
70}
71
72#[derive(Debug, Clone, PartialEq)]
73pub struct CursorStyleData {
74    pub color: Color,
75    pub highlight_color: Color,
76}
77
78impl Default for CursorStyleData {
79    fn default() -> Self {
80        Self {
81            color: Color::BLACK,
82            highlight_color: Color::from_rgb(87, 108, 188),
83        }
84    }
85}
86
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88#[derive(Debug, Clone, PartialEq, Hash)]
89pub struct TextStyleState {
90    pub font_size: FontSize,
91    pub color: Color,
92    pub text_align: TextAlign,
93    pub font_families: Vec<Cow<'static, str>>,
94    pub text_height: TextHeightBehavior,
95    pub text_overflow: TextOverflow,
96    pub text_shadows: Vec<TextShadow>,
97    pub font_slant: FontSlant,
98    pub font_weight: FontWeight,
99    pub font_width: FontWidth,
100}
101
102impl Default for TextStyleState {
103    fn default() -> Self {
104        Self {
105            font_size: FontSize::default(),
106            color: Color::BLACK,
107            text_align: TextAlign::default(),
108            font_families: Vec::new(),
109            text_height: TextHeightBehavior::default(),
110            text_overflow: TextOverflow::default(),
111            text_shadows: Vec::new(),
112            font_slant: FontSlant::default(),
113            font_weight: FontWeight::default(),
114            font_width: FontWidth::default(),
115        }
116    }
117}
118
119impl TextStyleState {
120    pub fn from_data(parent: &TextStyleState, data: &TextStyleData) -> Self {
121        let color = data.color.unwrap_or(parent.color);
122
123        let text_align = data.text_align.unwrap_or_default();
124        let text_height = data.text_height.unwrap_or_default();
125        let text_overflow = data.text_overflow.clone().unwrap_or_default();
126        let text_shadows = data.text_shadows.clone();
127
128        // Font values can be inherited
129        let font_size = data.font_size.unwrap_or(parent.font_size);
130        let font_slant = data.font_slant.unwrap_or(parent.font_slant);
131        let font_weight = data.font_weight.unwrap_or(parent.font_weight);
132        let font_width = data.font_width.unwrap_or(parent.font_width);
133        let mut font_families = data.font_families.clone();
134        font_families.extend_from_slice(&parent.font_families);
135
136        Self {
137            color,
138            text_align,
139            text_height,
140            text_overflow,
141            text_shadows,
142            font_size,
143            font_slant,
144            font_weight,
145            font_width,
146            font_families,
147        }
148    }
149
150    pub fn update(
151        &mut self,
152        node_id: NodeId,
153        parent_text_style: &Self,
154        element: &Rc<dyn ElementExt>,
155        layout: &mut Torin<NodeId>,
156    ) {
157        let text_style_data = element.text_style();
158
159        let text_style = Self::from_data(parent_text_style, &text_style_data);
160        let is_equal = *self == text_style;
161
162        *self = text_style;
163
164        if !is_equal {
165            // TODO: Only invalidate label and paragraphs
166            layout.invalidate(node_id);
167        }
168    }
169}
170
171#[derive(Debug, Clone, PartialEq, Default, Hash)]
172pub struct TextStyleData {
173    pub color: Option<Color>,
174    pub font_size: Option<FontSize>,
175    pub font_families: Vec<Cow<'static, str>>,
176    pub text_align: Option<TextAlign>,
177    pub text_height: Option<TextHeightBehavior>,
178    pub text_overflow: Option<TextOverflow>,
179    pub text_shadows: Vec<TextShadow>,
180    pub font_slant: Option<FontSlant>,
181    pub font_weight: Option<FontWeight>,
182    pub font_width: Option<FontWidth>,
183}
184
185#[derive(Debug, Default)]
186pub struct LayerState {
187    pub layer: i16,
188}
189
190impl LayerState {
191    pub fn create_for_root(node_id: NodeId, layers: &mut Layers) -> Self {
192        let layer = 0;
193
194        layers.insert_node_in_layer(node_id, layer);
195
196        Self { layer }
197    }
198
199    pub fn remove(self, node_id: NodeId, layers: &mut Layers) {
200        layers.remove_node_from_layer(&node_id, self.layer);
201    }
202
203    pub fn update(
204        &mut self,
205        parent_layer: &Self,
206        node_id: NodeId,
207        element: &Rc<dyn ElementExt>,
208        layers: &mut Layers,
209    ) {
210        let relative_layer = element.layer();
211
212        // Old
213        layers.remove_node_from_layer(&node_id, self.layer);
214
215        // New
216        self.layer = match relative_layer {
217            Layer::Relative(relative_layer) => parent_layer.layer + relative_layer + 1,
218            Layer::Overlay => i16::MAX / 2,
219        };
220        layers.insert_node_in_layer(node_id, self.layer);
221    }
222}
223
224#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
225pub enum Overflow {
226    #[default]
227    None,
228    Clip,
229}
230
231#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
232pub enum Interactive {
233    #[default]
234    Yes,
235    No,
236}
237
238#[derive(PartialEq, Default, Debug, Clone)]
239pub struct EffectState {
240    pub overflow: Overflow,
241    pub clips: Rc<[NodeId]>,
242
243    pub rotations: Rc<[NodeId]>,
244    pub rotation: Option<f32>,
245
246    pub scales: Rc<[NodeId]>,
247    pub scale: Option<Scale>,
248
249    pub opacities: Rc<[f32]>,
250
251    pub scrollables: Rc<[NodeId]>,
252
253    pub interactive: Interactive,
254}
255
256impl EffectState {
257    pub fn update(
258        &mut self,
259        parent_node_id: NodeId,
260        parent_effect_state: &Self,
261        node_id: NodeId,
262        effect_data: Option<Cow<'_, EffectData>>,
263    ) {
264        *self = Self {
265            overflow: Overflow::default(),
266            ..parent_effect_state.clone()
267        };
268
269        if parent_effect_state.overflow == Overflow::Clip {
270            let mut clips = parent_effect_state.clips.to_vec();
271            clips.push(parent_node_id);
272            if self.clips.as_ref() != clips {
273                self.clips = Rc::from(clips);
274            }
275        }
276
277        if let Some(effect_data) = effect_data {
278            self.overflow = effect_data.overflow;
279
280            if let Some(rotation) = effect_data.rotation {
281                let mut rotations = parent_effect_state.rotations.to_vec();
282                rotations.push(node_id);
283                self.rotation = Some(rotation);
284                if self.rotations.as_ref() != rotations {
285                    self.rotations = Rc::from(rotations);
286                }
287            }
288
289            if let Some(scale) = effect_data.scale {
290                let mut scales = parent_effect_state.scales.to_vec();
291                scales.push(node_id);
292                self.scale = Some(scale);
293                if self.scales.as_ref() != scales {
294                    self.scales = Rc::from(scales);
295                }
296            }
297
298            if let Some(opacity) = effect_data.opacity {
299                let mut opacities = parent_effect_state.opacities.to_vec();
300                opacities.push(opacity);
301                if self.opacities.as_ref() != opacities {
302                    self.opacities = Rc::from(opacities);
303                }
304            }
305
306            if effect_data.scrollable {
307                let mut scrolls = parent_effect_state.scrollables.to_vec();
308                scrolls.push(node_id);
309                if self.scrollables.as_ref() != scrolls {
310                    self.scrollables = Rc::from(scrolls);
311                }
312            }
313
314            self.interactive = effect_data.interactive;
315        }
316    }
317
318    pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
319        // Skip elements that are completely out of any their parent's viewport
320        for viewport_id in self.clips.iter() {
321            let viewport = layout.get(viewport_id).unwrap().visible_area();
322            if !viewport.intersects(area) {
323                return false;
324            }
325        }
326        true
327    }
328}
329
330#[derive(PartialEq, Clone)]
331pub struct AccessibilityState {
332    pub a11y_id: AccessibilityId,
333    pub a11y_focusable: Focusable,
334    pub a11y_member_of: Option<AccessibilityId>,
335}
336
337impl AccessibilityState {
338    pub fn create(
339        node_id: NodeId,
340        element: &Rc<dyn ElementExt>,
341        accessibility_diff: &mut AccessibilityDirtyNodes,
342        accessibility_generator: &AccessibilityGenerator,
343        accessibility_groups: &mut AccessibilityGroups,
344    ) -> Self {
345        let data = element.accessibility();
346
347        let a11y_id = if node_id == NodeId::ROOT {
348            ACCESSIBILITY_ROOT_ID
349        } else {
350            data.a11y_id
351                .unwrap_or_else(|| AccessibilityId(accessibility_generator.new_id()))
352        };
353
354        accessibility_diff.add_or_update(node_id);
355
356        if let Some(member_of) = data.builder.member_of() {
357            let group = accessibility_groups.entry(member_of).or_default();
358            // This is not perfect as it assumes that order of creation is the same as the UI order
359            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
360            // So for no we just push to the end of the vector
361            group.push(a11y_id);
362        }
363
364        if data.a11y_auto_focus {
365            accessibility_diff.request_focus(AccessibilityFocusStrategy::Node(a11y_id));
366        }
367
368        Self {
369            a11y_id,
370            a11y_focusable: data.a11y_focusable.clone(),
371            a11y_member_of: data.builder.member_of(),
372        }
373    }
374
375    pub fn remove(
376        self,
377        node_id: NodeId,
378        parent_id: NodeId,
379        accessibility_diff: &mut AccessibilityDirtyNodes,
380        accessibility_groups: &mut AccessibilityGroups,
381    ) {
382        accessibility_diff.remove(node_id, parent_id);
383
384        if let Some(member_of) = self.a11y_member_of {
385            let group = accessibility_groups.get_mut(&member_of).unwrap();
386            group.retain(|id| *id != self.a11y_id);
387        }
388    }
389
390    pub fn update(
391        &mut self,
392        node_id: NodeId,
393        element: &Rc<dyn ElementExt>,
394        accessibility_diff: &mut AccessibilityDirtyNodes,
395        accessibility_groups: &mut AccessibilityGroups,
396    ) {
397        let data = element.accessibility();
398
399        if let Some(member_of) = self.a11y_member_of
400            && self.a11y_member_of != data.builder.member_of()
401        {
402            let group = accessibility_groups.get_mut(&member_of).unwrap();
403            group.retain(|id| *id != self.a11y_id);
404        }
405
406        if let Some(a11y_id) = data.a11y_id
407            && self.a11y_id != a11y_id
408        {
409            accessibility_diff.add_or_update(node_id);
410            self.a11y_id = a11y_id;
411        }
412
413        if let Some(member_of) = data.builder.member_of() {
414            let group = accessibility_groups.entry(member_of).or_default();
415            // This is not perfect as it assumes that order of creation is the same as the UI order
416            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
417            // So for no we just push to the end of the vector
418            group.push(self.a11y_id);
419
420            self.a11y_member_of = Some(member_of);
421        }
422
423        self.a11y_focusable = data.a11y_focusable.clone();
424    }
425}
426
427#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
428#[derive(Debug, Default, Clone, PartialEq)]
429pub struct AccessibilityData {
430    pub a11y_id: Option<AccessibilityId>,
431    pub a11y_auto_focus: bool,
432    pub a11y_focusable: Focusable,
433    pub builder: accesskit::Node,
434}