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
238impl From<bool> for Interactive {
239    fn from(value: bool) -> Self {
240        match value {
241            true => Interactive::Yes,
242            false => Interactive::No,
243        }
244    }
245}
246
247#[derive(PartialEq, Default, Debug, Clone)]
248pub struct EffectState {
249    pub overflow: Overflow,
250    pub clips: Rc<[NodeId]>,
251
252    pub rotations: Rc<[NodeId]>,
253    pub rotation: Option<f32>,
254
255    pub scales: Rc<[NodeId]>,
256    pub scale: Option<Scale>,
257
258    pub opacities: Rc<[f32]>,
259
260    pub scrollables: Rc<[NodeId]>,
261
262    pub interactive: Interactive,
263}
264
265impl EffectState {
266    pub fn update(
267        &mut self,
268        parent_node_id: NodeId,
269        parent_effect_state: &Self,
270        node_id: NodeId,
271        effect_data: Option<Cow<'_, EffectData>>,
272    ) {
273        *self = Self {
274            overflow: Overflow::default(),
275            ..parent_effect_state.clone()
276        };
277
278        if parent_effect_state.overflow == Overflow::Clip {
279            let mut clips = parent_effect_state.clips.to_vec();
280            clips.push(parent_node_id);
281            if self.clips.as_ref() != clips {
282                self.clips = Rc::from(clips);
283            }
284        }
285
286        if let Some(effect_data) = effect_data {
287            self.overflow = effect_data.overflow;
288
289            if let Some(rotation) = effect_data.rotation {
290                let mut rotations = parent_effect_state.rotations.to_vec();
291                rotations.push(node_id);
292                self.rotation = Some(rotation);
293                if self.rotations.as_ref() != rotations {
294                    self.rotations = Rc::from(rotations);
295                }
296            }
297
298            if let Some(scale) = effect_data.scale {
299                let mut scales = parent_effect_state.scales.to_vec();
300                scales.push(node_id);
301                self.scale = Some(scale);
302                if self.scales.as_ref() != scales {
303                    self.scales = Rc::from(scales);
304                }
305            }
306
307            if let Some(opacity) = effect_data.opacity {
308                let mut opacities = parent_effect_state.opacities.to_vec();
309                opacities.push(opacity);
310                if self.opacities.as_ref() != opacities {
311                    self.opacities = Rc::from(opacities);
312                }
313            }
314
315            if effect_data.scrollable {
316                let mut scrolls = parent_effect_state.scrollables.to_vec();
317                scrolls.push(node_id);
318                if self.scrollables.as_ref() != scrolls {
319                    self.scrollables = Rc::from(scrolls);
320                }
321            }
322
323            self.interactive = effect_data.interactive;
324        }
325    }
326
327    pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
328        // Skip elements that are completely out of any their parent's viewport
329        for viewport_id in self.clips.iter() {
330            let viewport = layout.get(viewport_id).unwrap().visible_area();
331            if !viewport.intersects(area) {
332                return false;
333            }
334        }
335        true
336    }
337}
338
339#[derive(PartialEq, Clone)]
340pub struct AccessibilityState {
341    pub a11y_id: AccessibilityId,
342    pub a11y_focusable: Focusable,
343    pub a11y_member_of: Option<AccessibilityId>,
344}
345
346impl AccessibilityState {
347    pub fn create(
348        node_id: NodeId,
349        element: &Rc<dyn ElementExt>,
350        accessibility_diff: &mut AccessibilityDirtyNodes,
351        accessibility_generator: &AccessibilityGenerator,
352        accessibility_groups: &mut AccessibilityGroups,
353    ) -> Self {
354        let data = element.accessibility();
355
356        let a11y_id = if node_id == NodeId::ROOT {
357            ACCESSIBILITY_ROOT_ID
358        } else {
359            data.a11y_id
360                .unwrap_or_else(|| AccessibilityId(accessibility_generator.new_id()))
361        };
362
363        accessibility_diff.add_or_update(node_id);
364
365        if let Some(member_of) = data.builder.member_of() {
366            let group = accessibility_groups.entry(member_of).or_default();
367            // This is not perfect as it assumes that order of creation is the same as the UI order
368            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
369            // So for no we just push to the end of the vector
370            group.push(a11y_id);
371        }
372
373        if data.a11y_auto_focus {
374            accessibility_diff.request_focus(AccessibilityFocusStrategy::Node(a11y_id));
375        }
376
377        Self {
378            a11y_id,
379            a11y_focusable: data.a11y_focusable.clone(),
380            a11y_member_of: data.builder.member_of(),
381        }
382    }
383
384    pub fn remove(
385        self,
386        node_id: NodeId,
387        parent_id: NodeId,
388        accessibility_diff: &mut AccessibilityDirtyNodes,
389        accessibility_groups: &mut AccessibilityGroups,
390    ) {
391        accessibility_diff.remove(node_id, parent_id);
392
393        if let Some(member_of) = self.a11y_member_of {
394            let group = accessibility_groups.get_mut(&member_of).unwrap();
395            group.retain(|id| *id != self.a11y_id);
396        }
397    }
398
399    pub fn update(
400        &mut self,
401        node_id: NodeId,
402        element: &Rc<dyn ElementExt>,
403        accessibility_diff: &mut AccessibilityDirtyNodes,
404        accessibility_groups: &mut AccessibilityGroups,
405    ) {
406        let data = element.accessibility();
407
408        if let Some(member_of) = self.a11y_member_of
409            && self.a11y_member_of != data.builder.member_of()
410        {
411            let group = accessibility_groups.get_mut(&member_of).unwrap();
412            group.retain(|id| *id != self.a11y_id);
413        }
414
415        if let Some(a11y_id) = data.a11y_id
416            && self.a11y_id != a11y_id
417        {
418            accessibility_diff.add_or_update(node_id);
419            self.a11y_id = a11y_id;
420        }
421
422        if let Some(member_of) = data.builder.member_of() {
423            let group = accessibility_groups.entry(member_of).or_default();
424            // This is not perfect as it assumes that order of creation is the same as the UI order
425            // But we can't either assume that all the members are from the same parent so knowing their UI order gets trickier
426            // So for no we just push to the end of the vector
427            group.push(self.a11y_id);
428
429            self.a11y_member_of = Some(member_of);
430        }
431
432        self.a11y_focusable = data.a11y_focusable.clone();
433    }
434}
435
436#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
437#[derive(Debug, Default, Clone, PartialEq)]
438pub struct AccessibilityData {
439    pub a11y_id: Option<AccessibilityId>,
440    pub a11y_auto_focus: bool,
441    pub a11y_focusable: Focusable,
442    pub builder: accesskit::Node,
443}