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