1use std::{
2 borrow::Cow,
3 hash::Hash,
4 ops::{
5 Deref,
6 DerefMut,
7 },
8 rc::Rc,
9};
10
11use cursor_icon::CursorIcon;
12use torin::{
13 prelude::Area,
14 torin::Torin,
15};
16
17use crate::{
18 accessibility::{
19 dirty_nodes::AccessibilityDirtyNodes,
20 focusable::Focusable,
21 groups::AccessibilityGroups,
22 id::{
23 AccessibilityGenerator,
24 AccessibilityId,
25 },
26 tree::ACCESSIBILITY_ROOT_ID,
27 },
28 element::ElementExt,
29 layers::{
30 Layer,
31 Layers,
32 },
33 node_id::NodeId,
34 prelude::{
35 AccessibilityFocusStrategy,
36 CursorStyle,
37 },
38 style::{
39 border::Border,
40 color::Color,
41 corner_radius::CornerRadius,
42 fill::Fill,
43 font_feature::FontFeature,
44 font_size::FontSize,
45 font_slant::FontSlant,
46 font_weight::FontWeight,
47 font_width::FontWidth,
48 letter_spacing::LetterSpacing,
49 scale::Scale,
50 shadow::Shadow,
51 text_align::TextAlign,
52 text_decoration::TextDecoration,
53 text_height::TextHeightBehavior,
54 text_overflow::TextOverflow,
55 text_shadow::TextShadow,
56 transform_origin::TransformOrigin,
57 },
58};
59
60#[derive(Debug, Default, Clone, PartialEq)]
61pub struct LayoutData {
62 pub layout: torin::node::Node,
63}
64
65impl From<torin::node::Node> for LayoutData {
66 fn from(layout: torin::node::Node) -> Self {
67 LayoutData { layout }
68 }
69}
70
71impl Deref for LayoutData {
72 type Target = torin::node::Node;
73
74 fn deref(&self) -> &Self::Target {
75 &self.layout
76 }
77}
78
79impl DerefMut for LayoutData {
80 fn deref_mut(&mut self) -> &mut Self::Target {
81 &mut self.layout
82 }
83}
84
85#[derive(Debug, Default, Clone, PartialEq)]
86pub struct EffectData {
87 pub cursor: Option<CursorIcon>,
88 pub cursor_visible: Option<bool>,
89 pub overflow: Overflow,
90 pub rotation: Option<f32>,
91 pub scale: Option<Scale>,
92 pub transform_origin: TransformOrigin,
93 pub opacity: Option<f32>,
94 pub blur: Option<f32>,
95 pub scrollable: bool,
96 pub interactive: Interactive,
97}
98
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100#[derive(Debug, Default, Clone, PartialEq)]
101pub struct StyleState {
102 pub background: Fill,
103 pub corner_radius: CornerRadius,
104 pub borders: Vec<Border>,
105 pub shadows: Vec<Shadow>,
106}
107
108#[derive(Debug, Clone, PartialEq)]
109pub struct CursorStyleData {
110 pub color: Color,
111 pub highlight_color: Color,
112 pub style: CursorStyle,
113}
114
115impl Default for CursorStyleData {
116 fn default() -> Self {
117 Self {
118 color: Color::BLACK,
119 highlight_color: Color::from_argb(128, 87, 108, 188),
120 style: CursorStyle::default(),
121 }
122 }
123}
124
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126#[derive(Debug, Clone, PartialEq, Hash)]
127pub struct TextStyleState {
128 pub font_size: FontSize,
129 pub color: Fill,
130 pub text_align: TextAlign,
131 pub font_families: Vec<Cow<'static, str>>,
132 pub text_height: TextHeightBehavior,
133 pub text_overflow: TextOverflow,
134 pub text_shadows: Vec<TextShadow>,
135 pub text_decoration: TextDecoration,
136 pub font_slant: FontSlant,
137 pub font_weight: FontWeight,
138 pub font_width: FontWidth,
139 pub letter_spacing: LetterSpacing,
140 pub font_features: Vec<FontFeature>,
141}
142
143impl Default for TextStyleState {
144 fn default() -> Self {
145 Self {
146 font_size: FontSize::default(),
147 color: Fill::Color(Color::BLACK),
148 text_align: TextAlign::default(),
149 font_families: Vec::new(),
150 text_height: TextHeightBehavior::default(),
151 text_overflow: TextOverflow::default(),
152 text_shadows: Vec::new(),
153 text_decoration: TextDecoration::default(),
154 font_slant: FontSlant::default(),
155 font_weight: FontWeight::default(),
156 font_width: FontWidth::default(),
157 letter_spacing: LetterSpacing::default(),
158 font_features: Vec::new(),
159 }
160 }
161}
162
163impl TextStyleState {
164 pub fn from_data(parent: &TextStyleState, data: &TextStyleData) -> Self {
165 let color = data.color.as_ref().unwrap_or(&parent.color).clone();
166 let text_align = data.text_align.unwrap_or(parent.text_align);
167 let text_height = data.text_height.unwrap_or(parent.text_height);
168 let text_decoration = data.text_decoration.unwrap_or(parent.text_decoration);
169 let text_shadows = data
170 .text_shadows
171 .as_ref()
172 .unwrap_or(&parent.text_shadows)
173 .clone();
174 let font_size = data.font_size.unwrap_or(parent.font_size);
175 let font_slant = data.font_slant.unwrap_or(parent.font_slant);
176 let font_weight = data.font_weight.unwrap_or(parent.font_weight);
177 let font_width = data.font_width.unwrap_or(parent.font_width);
178 let letter_spacing = data.letter_spacing.unwrap_or(parent.letter_spacing);
179 let mut font_families = data.font_families.clone();
180 font_families.extend_from_slice(&parent.font_families);
181 let mut font_features = parent.font_features.clone();
182 font_features.extend_from_slice(&data.font_features);
183
184 let text_overflow = data.text_overflow.clone().unwrap_or_default();
186
187 Self {
188 color,
189 text_align,
190 text_height,
191 text_overflow,
192 text_shadows,
193 text_decoration,
194 font_size,
195 font_slant,
196 font_weight,
197 font_width,
198 letter_spacing,
199 font_families,
200 font_features,
201 }
202 }
203
204 pub fn update(
205 &mut self,
206 node_id: NodeId,
207 parent_text_style: &Self,
208 element: &Rc<dyn ElementExt>,
209 layout: &mut Torin<NodeId>,
210 ) -> bool {
211 let text_style_data = element.text_style();
212
213 let text_style = Self::from_data(parent_text_style, &text_style_data);
214 let is_equal = *self == text_style;
215
216 *self = text_style;
217
218 if !is_equal {
219 layout.invalidate(node_id);
221 }
222
223 !is_equal
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Default, Hash)]
228pub struct TextStyleData {
229 pub color: Option<Fill>,
230 pub font_size: Option<FontSize>,
231 pub font_families: Vec<Cow<'static, str>>,
232 pub text_align: Option<TextAlign>,
233 pub text_height: Option<TextHeightBehavior>,
234 pub text_overflow: Option<TextOverflow>,
235 pub text_shadows: Option<Vec<TextShadow>>,
236 pub text_decoration: Option<TextDecoration>,
237 pub font_slant: Option<FontSlant>,
238 pub font_weight: Option<FontWeight>,
239 pub font_width: Option<FontWidth>,
240 pub letter_spacing: Option<LetterSpacing>,
241 pub font_features: Vec<FontFeature>,
242}
243
244#[derive(Debug, Default)]
245pub struct LayerState {
246 pub layer: i16,
247}
248
249impl LayerState {
250 pub fn create_for_root(node_id: NodeId, layers: &mut Layers) -> Self {
251 let layer = 0;
252
253 layers.insert_node_in_layer(node_id, layer);
254
255 Self { layer }
256 }
257
258 pub fn remove(self, node_id: NodeId, layers: &mut Layers) {
259 layers.remove_node_from_layer(&node_id, self.layer);
260 }
261
262 pub fn update(
263 &mut self,
264 parent_layer: &Self,
265 node_id: NodeId,
266 element: &Rc<dyn ElementExt>,
267 layers: &mut Layers,
268 ) {
269 let relative_layer = element.layer();
270
271 layers.remove_node_from_layer(&node_id, self.layer);
273
274 self.layer = match relative_layer {
276 Layer::Relative(relative_layer) => parent_layer
277 .layer
278 .saturating_add(relative_layer)
279 .saturating_add(1),
280 Layer::Overlay => parent_layer.layer.saturating_add(i16::MAX / 16),
281 Layer::OverlayLevel(overlay_level) => {
282 (overlay_level.max(1) as i16).saturating_mul(i16::MAX / 16)
283 }
284 };
285 layers.insert_node_in_layer(node_id, self.layer);
286 }
287}
288
289#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
291pub enum Overflow {
292 #[default]
294 None,
295 Clip,
297}
298
299#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
303pub enum Interactive {
304 #[default]
306 Yes,
307 No,
309}
310
311impl From<bool> for Interactive {
312 fn from(value: bool) -> Self {
313 match value {
314 true => Interactive::Yes,
315 false => Interactive::No,
316 }
317 }
318}
319
320#[derive(PartialEq, Default, Debug, Clone)]
321pub struct EffectState {
322 pub overflow: Overflow,
323 pub clips: Rc<[NodeId]>,
324
325 pub rotations: Rc<[NodeId]>,
326 pub rotation: Option<f32>,
327
328 pub scales: Rc<[NodeId]>,
329 pub scale: Option<Scale>,
330
331 pub transform_origin: TransformOrigin,
332
333 pub opacities: Rc<[f32]>,
334
335 pub blur: Option<f32>,
336
337 pub scrollables: Rc<[NodeId]>,
338
339 pub interactive: Interactive,
340}
341
342impl EffectState {
343 pub fn update(
344 &mut self,
345 parent_node_id: NodeId,
346 parent_effect_state: &Self,
347 node_id: NodeId,
348 effect_data: Option<Cow<'_, EffectData>>,
349 layer: Layer,
350 ) {
351 *self = Self {
352 overflow: Overflow::default(),
353 blur: None,
354 rotation: None,
355 scale: None,
356 transform_origin: TransformOrigin::default(),
357 ..parent_effect_state.clone()
358 };
359
360 match layer {
361 Layer::Overlay | Layer::OverlayLevel(_) => {
362 self.clips = Rc::default();
363 }
364 Layer::Relative(_) if parent_effect_state.overflow == Overflow::Clip => {
365 let mut clips = parent_effect_state.clips.to_vec();
366 clips.push(parent_node_id);
367 if self.clips.as_ref() != clips {
368 self.clips = Rc::from(clips);
369 }
370 }
371 _ => {}
372 }
373
374 if let Some(effect_data) = effect_data {
375 self.overflow = effect_data.overflow;
376 self.blur = effect_data.blur;
377 self.transform_origin = effect_data.transform_origin;
378
379 if let Some(rotation) = effect_data.rotation {
380 let mut rotations = parent_effect_state.rotations.to_vec();
381 rotations.push(node_id);
382 self.rotation = Some(rotation);
383 if self.rotations.as_ref() != rotations {
384 self.rotations = Rc::from(rotations);
385 }
386 }
387
388 if let Some(scale) = effect_data.scale {
389 let mut scales = parent_effect_state.scales.to_vec();
390 scales.push(node_id);
391 self.scale = Some(scale);
392 if self.scales.as_ref() != scales {
393 self.scales = Rc::from(scales);
394 }
395 }
396
397 if let Some(opacity) = effect_data.opacity {
398 let mut opacities = parent_effect_state.opacities.to_vec();
399 opacities.push(opacity);
400 if self.opacities.as_ref() != opacities {
401 self.opacities = Rc::from(opacities);
402 }
403 }
404
405 if effect_data.scrollable {
406 let mut scrolls = parent_effect_state.scrollables.to_vec();
407 scrolls.push(node_id);
408 if self.scrollables.as_ref() != scrolls {
409 self.scrollables = Rc::from(scrolls);
410 }
411 }
412
413 if effect_data.interactive == Interactive::No {
414 self.interactive = Interactive::No;
415 }
416 }
417 }
418
419 pub fn is_visible(&self, layout: &Torin<NodeId>, area: &Area) -> bool {
420 for viewport_id in self.clips.iter() {
422 let viewport = layout.get(viewport_id).unwrap().visible_area();
423 if !viewport.intersects(area) {
424 return false;
425 }
426 }
427 true
428 }
429}
430
431#[derive(PartialEq, Clone)]
432pub struct AccessibilityState {
433 pub a11y_id: AccessibilityId,
434 pub a11y_focusable: Focusable,
435 pub a11y_member_of: Option<AccessibilityId>,
436 a11y_intrinsic_id: AccessibilityId,
437}
438
439impl AccessibilityState {
440 pub fn create(
441 node_id: NodeId,
442 element: &Rc<dyn ElementExt>,
443 accessibility_diff: &mut AccessibilityDirtyNodes,
444 accessibility_generator: &AccessibilityGenerator,
445 accessibility_groups: &mut AccessibilityGroups,
446 ) -> Self {
447 let data = element.accessibility();
448
449 let a11y_intrinsic_id = if node_id == NodeId::ROOT {
450 ACCESSIBILITY_ROOT_ID
451 } else {
452 AccessibilityId(accessibility_generator.new_id())
453 };
454 let a11y_id = data
455 .a11y_id
456 .filter(|_| node_id != NodeId::ROOT)
457 .unwrap_or(a11y_intrinsic_id);
458
459 accessibility_diff.add_or_update(node_id);
460
461 if let Some(member_of) = data.builder.member_of() {
462 let group = accessibility_groups.entry(member_of).or_default();
463 group.push(a11y_id);
467 }
468
469 if data.a11y_auto_focus {
470 accessibility_diff.request_auto_focus(AccessibilityFocusStrategy::Node(a11y_id));
471 }
472
473 Self {
474 a11y_id,
475 a11y_focusable: data.a11y_focusable.clone(),
476 a11y_member_of: data.builder.member_of(),
477 a11y_intrinsic_id,
478 }
479 }
480
481 pub fn remove(
482 self,
483 node_id: NodeId,
484 parent_id: NodeId,
485 accessibility_diff: &mut AccessibilityDirtyNodes,
486 accessibility_groups: &mut AccessibilityGroups,
487 ) {
488 accessibility_diff.remove(node_id, parent_id);
489
490 if let Some(member_of) = self.a11y_member_of {
491 let group = accessibility_groups.get_mut(&member_of).unwrap();
492 group.retain(|id| *id != self.a11y_id);
493 }
494 }
495
496 pub fn update(
497 &mut self,
498 node_id: NodeId,
499 element: &Rc<dyn ElementExt>,
500 accessibility_diff: &mut AccessibilityDirtyNodes,
501 accessibility_groups: &mut AccessibilityGroups,
502 ) {
503 let data = element.accessibility();
504
505 if let Some(member_of) = self.a11y_member_of
506 && self.a11y_member_of != data.builder.member_of()
507 {
508 let group = accessibility_groups.get_mut(&member_of).unwrap();
509 group.retain(|id| *id != self.a11y_id);
510 }
511
512 self.a11y_id = data.a11y_id.unwrap_or(self.a11y_intrinsic_id);
513 accessibility_diff.add_or_update(node_id);
514
515 if let Some(member_of) = data.builder.member_of() {
516 let group = accessibility_groups.entry(member_of).or_default();
517 group.push(self.a11y_id);
521
522 self.a11y_member_of = Some(member_of);
523 }
524
525 self.a11y_focusable = data.a11y_focusable.clone();
526 }
527}
528
529#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
530#[derive(Debug, Default, Clone, PartialEq)]
531pub struct AccessibilityData {
532 pub a11y_id: Option<AccessibilityId>,
533 pub a11y_auto_focus: bool,
534 pub a11y_focusable: Focusable,
535 pub builder: accesskit::Node,
536}