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