tuix_core/systems/
style_system.rs1use crate::{BoundingBox, Entity, Event, TreeExt, IntoParentIterator, State, WindowEvent};
2
3use crate::tree::*;
4use crate::state::animation::*;
5
6pub fn apply_z_ordering(state: &mut State, tree: &Tree) {
7 for entity in tree.into_iter() {
8 if entity == Entity::root() {
9 continue;
10 }
11
12 let parent = tree.get_parent(entity).unwrap();
13
14 if let Some(z_order) = state.style.z_order.get(entity) {
15 state.data.set_z_order(entity, *z_order);
16 } else {
17 let parent_z_order = state.data.get_z_order(parent);
18 state.data.set_z_order(entity, parent_z_order);
19 }
20 }
21}
22
23pub fn apply_clipping(state: &mut State, tree: &Tree) {
24 for entity in tree.into_iter() {
26 if entity == Entity::root() {
27 continue;
28 }
29
30 let parent = tree.get_parent(entity).unwrap();
31
32 let mut parent_clip_region = state.data.get_clip_region(parent);
33 let parent_border_width = state.style.border_width.get(parent).cloned().unwrap_or_default().value_or(0.0, 0.0);
34
35 parent_clip_region.x += parent_border_width / 2.0;
37 parent_clip_region.y += parent_border_width / 2.0;
38 parent_clip_region.w -= parent_border_width;
39 parent_clip_region.h -= parent_border_width;
40
41
42
43 let root_clip_region = state.data.get_clip_region(Entity::root());
44
45 if entity.get_overflow(state) == Overflow::Hidden {
46 if let Some(clip_widget) = state.style.clip_widget.get(entity).cloned() {
47 let clip_widget_border_width = state.style.border_width.get(clip_widget).cloned().unwrap_or_default().value_or(0.0, 0.0);
48 let clip_x = state.data.get_posx(clip_widget) + clip_widget_border_width;
49 let clip_y = state.data.get_posy(clip_widget) + clip_widget_border_width;
50 let clip_w = state.data.get_width(clip_widget) - 2.0 * clip_widget_border_width;
51 let clip_h = state.data.get_height(clip_widget) - 2.0 * clip_widget_border_width;
52
53 let mut intersection = BoundingBox::default();
54 intersection.x = clip_x.max(parent_clip_region.x);
55 intersection.y = clip_y.max(parent_clip_region.y);
56
57 intersection.w = if clip_x + clip_w < parent_clip_region.x + parent_clip_region.w {
58 clip_x + clip_w - intersection.x
59 } else {
60 parent_clip_region.x + parent_clip_region.w - intersection.x
61 };
62
63 intersection.h = if clip_y + clip_h < parent_clip_region.y + parent_clip_region.h {
64 clip_y + clip_h - intersection.y
65 } else {
66 parent_clip_region.y + parent_clip_region.h - intersection.y
67 };
68
69 state.data.set_clip_region(entity, intersection);
70 } else {
71 state.data.set_clip_region(entity, parent_clip_region);
72 }
73 } else {
74 state.data.set_clip_region(entity, root_clip_region);
75 }
76
77 }
80}
81
82pub fn apply_visibility(state: &mut State, tree: &Tree) {
83 let mut draw_tree: Vec<Entity> = tree.into_iter().collect();
85 draw_tree.sort_by_cached_key(|entity| state.data.get_z_order(*entity));
86
87 for widget in draw_tree.into_iter() {
88 let visibility = state
89 .style
90 .visibility
91 .get(widget)
92 .cloned()
93 .unwrap_or_default();
94 state.data.set_visibility(widget, visibility);
95
96 let opacity = state.style.opacity.get(widget).cloned().unwrap_or_default();
97
98 state.data.set_opacity(widget, opacity.0);
99
100 let display = state.style.display.get(widget).cloned().unwrap_or_default();
101
102 if display == Display::None {
103 state.data.set_visibility(widget, Visibility::Invisible);
104 }
105
106 if let Some(parent) = widget.parent(tree) {
107 let parent_visibility = state.data.get_visibility(parent);
108 if parent_visibility == Visibility::Invisible {
109 state.data.set_visibility(widget, Visibility::Invisible);
110 }
111 let parent_display = state.style.display.get(parent).cloned().unwrap_or_default();
112 if parent_display == Display::None {
113 state.data.set_visibility(widget, Visibility::Invisible);
114 }
115
116 let parent_opacity = state.data.get_opacity(parent);
117
118 let opacity = state.style.opacity.get(widget).cloned().unwrap_or_default();
119
120 state.data.set_opacity(widget, opacity.0 * parent_opacity);
121 }
122 }
123}
124
125fn check_match(state: &State, entity: Entity, selector: &Selector) -> bool {
127 let mut entity_selector = Selector::new();
129
130 entity_selector.element = state.style.elements.get(entity).cloned();
140
141 if let Some(class_list) = state.style.classes.get(entity) {
143 entity_selector.classes = class_list.clone();
144 }
145
146 entity_selector.pseudo_classes = state
148 .style
149 .pseudo_classes
150 .get(entity)
151 .cloned()
152 .unwrap_or_default();
153
154 if state.active == entity {
155 entity_selector.pseudo_classes.insert(PseudoClasses::ACTIVE);
156 }
157
158 entity_selector.pseudo_classes.set(PseudoClasses::FOCUS, state.focused == entity);
159
160 return selector.matches(&entity_selector);
161}
162
163pub fn apply_styles(state: &mut State, tree: &Tree) {
164 for entity in tree.into_iter() {
167 if entity == Entity::root() {
169 continue;
170 }
171
172 let mut matched_rules: Vec<usize> = Vec::new();
174
175 'rule_loop: for (index, rule) in state.style.rules.iter().enumerate() {
177 let mut relation_entity = entity;
178 'selector_loop: for rule_selector in rule.selectors.iter().rev() {
181 match rule_selector.relation {
183 Relation::None => {
184 if !check_match(state, entity, rule_selector) {
185 continue 'rule_loop;
186 }
187 }
188
189 Relation::Parent => {
190 if let Some(parent) = relation_entity.parent(tree) {
194 if !check_match(state, parent, rule_selector) {
195 continue 'rule_loop;
196 }
197
198 relation_entity = parent;
199 } else {
200 continue 'rule_loop;
201 }
202 }
203
204 Relation::Ancestor => {
205 for ancestor in relation_entity.parent_iter(tree) {
210 if ancestor == relation_entity {
211 continue;
212 }
213
214 if check_match(state, ancestor, rule_selector) {
215 relation_entity = ancestor;
216
217 continue 'selector_loop;
218 }
219 }
220
221 continue 'rule_loop;
222 }
223 }
224 }
225
226 matched_rules.push(index);
228 }
229
230 if matched_rules.len() == 0 {
233 continue;
234 }
235
236 let mut should_relayout = false;
237 let mut should_redraw = false;
238
239 if state.style.display.link_rule(entity, &matched_rules) {
241 should_relayout = true;
243 should_redraw = true;
244 }
245 if state.style.visibility.link_rule(entity, &matched_rules) {
246 should_relayout = true;
248 should_redraw = true;
249 }
250
251 if state.style.z_order.link_rule(entity, &matched_rules) {
252 should_relayout = true;
254 should_redraw = true;
255 }
256
257 state.style.overflow.link_rule(entity, &matched_rules);
259
260 if state.style.opacity.link_rule(entity, &matched_rules) {
262 should_relayout = true;
264 should_redraw = true;
265 }
266
267 if state.style.left.link_rule(entity, &matched_rules) {
268 should_relayout = true;
270 should_redraw = true;
271 }
272
273 if state.style.right.link_rule(entity, &matched_rules) {
274 should_relayout = true;
276 should_redraw = true;
277 }
278
279 if state.style.top.link_rule(entity, &matched_rules) {
280 should_relayout = true;
282 should_redraw = true;
283 }
284
285 if state.style.bottom.link_rule(entity, &matched_rules) {
286 should_relayout = true;
288 should_redraw = true;
289 }
290
291 if state.style.width.link_rule(entity, &matched_rules) {
293 should_relayout = true;
295 should_redraw = true;
296 }
297
298 if state.style.height.link_rule(entity, &matched_rules) {
299 should_relayout = true;
301 should_redraw = true;
302 }
303
304 if state.style.max_width.link_rule(entity, &matched_rules) {
306 should_relayout = true;
308 should_redraw = true;
309 }
310
311 if state.style.min_width.link_rule(entity, &matched_rules) {
312 should_relayout = true;
314 should_redraw = true;
315 }
316
317 if state.style.max_height.link_rule(entity, &matched_rules) {
318 should_relayout = true;
320 should_redraw = true;
321 }
322
323 if state.style.min_height.link_rule(entity, &matched_rules) {
324 should_relayout = true;
326 should_redraw = true;
327 }
328
329 if state.style.border_width.link_rule(entity, &matched_rules) {
331 should_relayout = true;
333 should_redraw = true;
334 }
335
336 if state.style.border_color.link_rule(entity, &matched_rules) {
337 should_redraw = true;
339 }
340
341 if state.style.border_shape_top_left.link_rule(entity, &matched_rules) {
342 should_redraw = true;
343 }
344
345 if state.style.border_shape_top_right.link_rule(entity, &matched_rules) {
346 should_redraw = true;
347 }
348
349 if state.style.border_shape_bottom_left.link_rule(entity, &matched_rules) {
350 should_redraw = true;
351 }
352
353 if state.style.border_shape_bottom_right.link_rule(entity, &matched_rules) {
354 should_redraw = true;
355 }
356
357 if state
358 .style
359 .border_radius_top_left
360 .link_rule(entity, &matched_rules)
361 {
362 should_redraw = true;
364 }
365
366 if state
367 .style
368 .border_radius_top_right
369 .link_rule(entity, &matched_rules)
370 {
371 should_redraw = true;
373 }
374
375 if state
376 .style
377 .border_radius_bottom_left
378 .link_rule(entity, &matched_rules)
379 {
380 should_redraw = true;
382 }
383
384 if state
385 .style
386 .border_radius_bottom_right
387 .link_rule(entity, &matched_rules)
388 {
389 should_redraw = true;
391 }
392
393 if state.style.layout_type.link_rule(entity, &matched_rules) {
394 should_relayout = true;
396 should_redraw = true;
397 }
398
399 if state
400 .style
401 .positioning_type
402 .link_rule(entity, &matched_rules)
403 {
404 should_relayout = true;
406 should_redraw = true;
407 }
408
409 if state
411 .style
412 .background_color
413 .link_rule(entity, &matched_rules)
414 {
415 should_redraw = true;
417 }
418
419 if state
420 .style
421 .background_image
422 .link_rule(entity, &matched_rules)
423 {
424 should_redraw = true;
426 }
427
428 if state.style.font_color.link_rule(entity, &matched_rules) {
430 should_redraw = true;
432 }
433
434 if state.style.font_size.link_rule(entity, &matched_rules) {
435 should_redraw = true;
437 }
438
439 if state
441 .style
442 .outer_shadow_h_offset
443 .link_rule(entity, &matched_rules)
444 {
445 should_redraw = true;
447 }
448
449 if state
450 .style
451 .outer_shadow_v_offset
452 .link_rule(entity, &matched_rules)
453 {
454 should_redraw = true;
456 }
457
458 if state
459 .style
460 .outer_shadow_blur
461 .link_rule(entity, &matched_rules)
462 {
463 should_redraw = true;
465 }
466
467 if state
468 .style
469 .outer_shadow_color
470 .link_rule(entity, &matched_rules)
471 {
472 should_redraw = true;
474 }
475
476 if state
478 .style
479 .inner_shadow_h_offset
480 .link_rule(entity, &matched_rules)
481 {
482 should_redraw = true;
484 }
485
486 if state
487 .style
488 .inner_shadow_v_offset
489 .link_rule(entity, &matched_rules)
490 {
491 should_redraw = true;
493 }
494
495 if state
496 .style
497 .inner_shadow_blur
498 .link_rule(entity, &matched_rules)
499 {
500 should_redraw = true;
502 }
503
504 if state
505 .style
506 .inner_shadow_color
507 .link_rule(entity, &matched_rules)
508 {
509 should_redraw = true;
511 }
512
513 if state.style.child_left.link_rule(entity, &matched_rules) {
514 should_relayout = true;
515 should_redraw = true;
516 }
517
518 if state.style.child_right.link_rule(entity, &matched_rules) {
519 should_relayout = true;
520 should_redraw = true;
521 }
522
523 if state.style.child_top.link_rule(entity, &matched_rules) {
524 should_relayout = true;
525 should_redraw = true;
526 }
527
528 if state.style.child_bottom.link_rule(entity, &matched_rules) {
529 should_relayout = true;
530 should_redraw = true;
531 }
532
533 if state.style.row_between.link_rule(entity, &matched_rules) {
534 should_relayout = true;
535 should_redraw = true;
536 }
537
538 if state.style.col_between.link_rule(entity, &matched_rules) {
539 should_relayout = true;
540 should_redraw = true;
541 }
542
543
544 for rule_index in matched_rules.iter() {
545 if let Some(rule) = state.style.rules.get(*rule_index as usize).cloned() {
547 for property in rule.properties.iter() {
548 match property {
549 Property::Unknown(ident, prop) => {
550 if let Some(mut event_handler) = state.event_handlers.remove(&entity) {
551 event_handler.on_style(state, entity, (ident.clone(), prop.clone()));
552
553 state.event_handlers.insert(entity, event_handler);
554 }
555 }
556
557 _=> {}
558 }
559 }
560 }
561 }
562
563 if should_relayout {
564 state.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
565 }
567
568 if should_redraw {
569 state.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
570 }
572 }
573}