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