tuix_core/systems/
style_system.rs

1use 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    //println!("Apply Clipping");
25    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        //println!("Parent border width: {}", parent_border_width);
36        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        //let clip_region = state.data.get_clip_region(entity);
78        //println!("Entity: {}  Clip Region: {:?}", entity, clip_region);
79    }
80}
81
82pub fn apply_visibility(state: &mut State, tree: &Tree) {
83    //println!("Apply Visibility");
84    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
125// Returns true if the widget matches the selector
126fn check_match(state: &State, entity: Entity, selector: &Selector) -> bool {
127    // Construct the entity selector
128    let mut entity_selector = Selector::new();
129
130    // Get the entity id from state
131    //entity_selector.id = state.style.ids.get(entity).cloned();
132    // let mut s = DefaultHasher::new();
133    // entity_selector.id = state.style.ids.get_by_right(&entity).map(|f| {
134    //     f.hash(&mut s);
135    //     s.finish()
136    // });
137
138    // Get the entity element from state
139    entity_selector.element = state.style.elements.get(entity).cloned();
140
141    // Get the entity class list from state
142    if let Some(class_list) = state.style.classes.get(entity) {
143        entity_selector.classes = class_list.clone();
144    }
145
146    // Set the pseudoclass selectors
147    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    //println!("RESTYLE");
165    // Loop through all entities
166    for entity in tree.into_iter() {
167        // Skip the root
168        if entity == Entity::root() {
169            continue;
170        }
171
172        // Create a list of style rules that match this entity
173        let mut matched_rules: Vec<usize> = Vec::new();
174
175        // Loop through all of the style rules
176        'rule_loop: for (index, rule) in state.style.rules.iter().enumerate() {
177            let mut relation_entity = entity;
178            // Loop through selectors (Should be from right to left)
179            // All the selectors need to match for the rule to apply
180            'selector_loop: for rule_selector in rule.selectors.iter().rev() {
181                // Get the relation of the selector
182                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                        // Get the parent
191                        // Contrust the selector for the parent
192                        // Check if the parent selector matches the rule_seletor
193                        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                        // Walk up the tree
206                        // Check if each entity matches the selector
207                        // If any of them match, move on to the next selector
208                        // If none of them do, move on to the next rule
209                        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            // If all the selectors match then add the rule to the matched rules list
227            matched_rules.push(index);
228        }
229
230        //println!("Entity: {}, Matched Rules: {:?}", entity, &matched_rules);
231
232        if matched_rules.len() == 0 {
233            continue;
234        }
235
236        let mut should_relayout = false;
237        let mut should_redraw = false;
238
239        // Display
240        if state.style.display.link_rule(entity, &matched_rules) {
241            //println!("1");
242            should_relayout = true;
243            should_redraw = true;
244        }
245        if state.style.visibility.link_rule(entity, &matched_rules) {
246            //println!("2");
247            should_relayout = true;
248            should_redraw = true;
249        }
250
251        if state.style.z_order.link_rule(entity, &matched_rules) {
252            //println!("3");
253            should_relayout = true;
254            should_redraw = true;
255        }
256
257        // Currently doesn't do anything - TODO
258        state.style.overflow.link_rule(entity, &matched_rules);
259
260        // Opacity
261        if state.style.opacity.link_rule(entity, &matched_rules) {
262            //println!("4");
263            should_relayout = true;
264            should_redraw = true;
265        }
266
267        if state.style.left.link_rule(entity, &matched_rules) {
268            //println!("6");
269            should_relayout = true;
270            should_redraw = true;
271        }
272
273        if state.style.right.link_rule(entity, &matched_rules) {
274            //println!("7");
275            should_relayout = true;
276            should_redraw = true;
277        }
278
279        if state.style.top.link_rule(entity, &matched_rules) {
280            //println!("8");
281            should_relayout = true;
282            should_redraw = true;
283        }
284
285        if state.style.bottom.link_rule(entity, &matched_rules) {
286            //println!("9");
287            should_relayout = true;
288            should_redraw = true;
289        }
290
291        // Size
292        if state.style.width.link_rule(entity, &matched_rules) {
293            //println!("10");
294            should_relayout = true;
295            should_redraw = true;
296        }
297
298        if state.style.height.link_rule(entity, &matched_rules) {
299            //println!("11");
300            should_relayout = true;
301            should_redraw = true;
302        }
303
304        // Size Constraints
305        if state.style.max_width.link_rule(entity, &matched_rules) {
306            //println!("12");
307            should_relayout = true;
308            should_redraw = true;
309        }
310
311        if state.style.min_width.link_rule(entity, &matched_rules) {
312            //println!("13");
313            should_relayout = true;
314            should_redraw = true;
315        }
316
317        if state.style.max_height.link_rule(entity, &matched_rules) {
318            //println!("14");
319            should_relayout = true;
320            should_redraw = true;
321        }
322
323        if state.style.min_height.link_rule(entity, &matched_rules) {
324            //println!("15");
325            should_relayout = true;
326            should_redraw = true;
327        }
328
329        // Border
330        if state.style.border_width.link_rule(entity, &matched_rules) {
331            //println!("24");
332            should_relayout = true;
333            should_redraw = true;
334        }
335
336        if state.style.border_color.link_rule(entity, &matched_rules) {
337            //println!("25");
338            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            //println!("26");
363            should_redraw = true;
364        }
365
366        if state
367            .style
368            .border_radius_top_right
369            .link_rule(entity, &matched_rules)
370        {
371            //println!("27");
372            should_redraw = true;
373        }
374
375        if state
376            .style
377            .border_radius_bottom_left
378            .link_rule(entity, &matched_rules)
379        {
380            //println!("28");
381            should_redraw = true;
382        }
383
384        if state
385            .style
386            .border_radius_bottom_right
387            .link_rule(entity, &matched_rules)
388        {
389            //println!("29");
390            should_redraw = true;
391        }
392
393        if state.style.layout_type.link_rule(entity, &matched_rules) {
394            //println!("30");
395            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            //println!("30");
405            should_relayout = true;
406            should_redraw = true;
407        }
408
409        // Background
410        if state
411            .style
412            .background_color
413            .link_rule(entity, &matched_rules)
414        {
415            //println!("41");
416            should_redraw = true;
417        }
418
419        if state
420            .style
421            .background_image
422            .link_rule(entity, &matched_rules)
423        {
424            //println!("42");
425            should_redraw = true;
426        }
427
428        // Font
429        if state.style.font_color.link_rule(entity, &matched_rules) {
430            //println!("43");
431            should_redraw = true;
432        }
433
434        if state.style.font_size.link_rule(entity, &matched_rules) {
435            //println!("44");
436            should_redraw = true;
437        }
438
439        // Outer Shadow
440        if state
441            .style
442            .outer_shadow_h_offset
443            .link_rule(entity, &matched_rules)
444        {
445            //println!("45");
446            should_redraw = true;
447        }
448
449        if state
450            .style
451            .outer_shadow_v_offset
452            .link_rule(entity, &matched_rules)
453        {
454            //println!("46");
455            should_redraw = true;
456        }
457
458        if state
459            .style
460            .outer_shadow_blur
461            .link_rule(entity, &matched_rules)
462        {
463            //println!("47");
464            should_redraw = true;
465        }
466
467        if state
468            .style
469            .outer_shadow_color
470            .link_rule(entity, &matched_rules)
471        {
472            //println!("48");
473            should_redraw = true;
474        }
475
476        // Inner Shadow
477        if state
478            .style
479            .inner_shadow_h_offset
480            .link_rule(entity, &matched_rules)
481        {
482            //println!("45");
483            should_redraw = true;
484        }
485
486        if state
487            .style
488            .inner_shadow_v_offset
489            .link_rule(entity, &matched_rules)
490        {
491            //println!("46");
492            should_redraw = true;
493        }
494
495        if state
496            .style
497            .inner_shadow_blur
498            .link_rule(entity, &matched_rules)
499        {
500            //println!("47");
501            should_redraw = true;
502        }
503
504        if state
505            .style
506            .inner_shadow_color
507            .link_rule(entity, &matched_rules)
508        {
509            //println!("48");
510            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            // TODO - remove cloned
546            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            //state.needs_relayout = true;
566        }
567
568        if should_redraw {
569            state.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
570            //state.needs_redraw = true;
571        }
572    }
573}