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