rustmotion_core/css/
cascade.rs1use super::style::CssStyle;
2
3pub fn inherit_from(parent: &CssStyle, child: &mut CssStyle) {
4 if child.color.is_none() {
5 child.color = parent.color.clone();
6 }
7 if child.font_family.is_none() {
8 child.font_family = parent.font_family.clone();
9 }
10 if child.font_size.is_none() {
11 child.font_size = parent.font_size.clone();
12 }
13 if child.font_weight.is_none() {
14 child.font_weight = parent.font_weight.clone();
15 }
16 if child.font_style.is_none() {
17 child.font_style = parent.font_style;
18 }
19 if child.line_height.is_none() {
20 child.line_height = parent.line_height.clone();
21 }
22 if child.letter_spacing.is_none() {
23 child.letter_spacing = parent.letter_spacing.clone();
24 }
25 if child.text_align.is_none() {
26 child.text_align = parent.text_align;
27 }
28 if child.white_space.is_none() {
29 child.white_space = parent.white_space;
30 }
31 if child.overflow_wrap.is_none() {
32 child.overflow_wrap = parent.overflow_wrap;
33 }
34 if child.visibility.is_none() {
35 child.visibility = parent.visibility;
36 }
37 if child.text_decoration.is_none() {
38 child.text_decoration = parent.text_decoration.clone();
39 }
40}
41
42pub fn overlay_resolved_typography(resolved: &CssStyle, own: &mut CssStyle) {
43 if resolved.color.is_some() {
44 own.color = resolved.color.clone();
45 }
46 if resolved.font_family.is_some() {
47 own.font_family = resolved.font_family.clone();
48 }
49 if resolved.font_size.is_some() {
50 own.font_size = resolved.font_size.clone();
51 }
52 if resolved.font_weight.is_some() {
53 own.font_weight = resolved.font_weight.clone();
54 }
55 if resolved.font_style.is_some() {
56 own.font_style = resolved.font_style;
57 }
58 if resolved.line_height.is_some() {
59 own.line_height = resolved.line_height.clone();
60 }
61 if resolved.letter_spacing.is_some() {
62 own.letter_spacing = resolved.letter_spacing.clone();
63 }
64 if resolved.text_align.is_some() {
65 own.text_align = resolved.text_align;
66 }
67 if resolved.white_space.is_some() {
68 own.white_space = resolved.white_space;
69 }
70 if resolved.overflow_wrap.is_some() {
71 own.overflow_wrap = resolved.overflow_wrap;
72 }
73 if resolved.visibility.is_some() {
74 own.visibility = resolved.visibility;
75 }
76 if resolved.text_decoration.is_some() {
77 own.text_decoration = resolved.text_decoration.clone();
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::css::style::{Color, Display, FontStyle, TextAlign};
85
86 #[test]
87 fn child_inherits_color_when_unset() {
88 let parent = CssStyle {
89 color: Some(Color::String("#ff0000".into())),
90 ..Default::default()
91 };
92 let mut child = CssStyle::default();
93 inherit_from(&parent, &mut child);
94 assert!(matches!(child.color, Some(Color::String(_))));
95 }
96
97 #[test]
98 fn child_overrides_parent_color() {
99 let parent = CssStyle {
100 color: Some(Color::String("#ff0000".into())),
101 ..Default::default()
102 };
103 let mut child = CssStyle {
104 color: Some(Color::String("#00ff00".into())),
105 ..Default::default()
106 };
107 inherit_from(&parent, &mut child);
108 match child.color {
109 Some(Color::String(s)) => assert_eq!(s, "#00ff00"),
110 _ => panic!("expected child color preserved"),
111 }
112 }
113
114 #[test]
115 fn non_inheritable_not_propagated() {
116 let parent = CssStyle {
117 display: Some(Display::Flex),
118 ..Default::default()
119 };
120 let mut child = CssStyle::default();
121 inherit_from(&parent, &mut child);
122 assert!(child.display.is_none());
123 }
124
125 #[test]
126 fn font_props_inherited() {
127 let parent = CssStyle {
128 font_family: Some("Arial".into()),
129 font_style: Some(FontStyle::Italic),
130 text_align: Some(TextAlign::Center),
131 ..Default::default()
132 };
133 let mut child = CssStyle::default();
134 inherit_from(&parent, &mut child);
135 assert_eq!(child.font_family.as_deref(), Some("Arial"));
136 assert_eq!(child.font_style, Some(FontStyle::Italic));
137 assert_eq!(child.text_align, Some(TextAlign::Center));
138 }
139
140 #[test]
141 fn overlay_resolved_typography_replaces_a_component_own_static_font_size() {
142 use crate::css::units::Length;
143
144 let resolved = CssStyle {
145 font_size: Some(Length::Px(120.0)),
146 ..Default::default()
147 };
148 let mut own = CssStyle {
149 font_size: Some(Length::Px(20.0)),
150 ..Default::default()
151 };
152 overlay_resolved_typography(&resolved, &mut own);
153 assert_eq!(
154 own.font_size,
155 Some(Length::Px(120.0)),
156 "the box's own already-animated resolved font-size must win over the component's \
157 declared static one, unlike inherit_from's fill-only-if-none rule"
158 );
159 }
160
161 #[test]
162 fn overlay_resolved_typography_replaces_a_component_own_static_letter_spacing() {
163 use crate::css::units::Length;
164
165 let resolved = CssStyle {
166 letter_spacing: Some(Length::Px(18.0)),
167 ..Default::default()
168 };
169 let mut own = CssStyle {
170 letter_spacing: Some(Length::Px(0.0)),
171 ..Default::default()
172 };
173 overlay_resolved_typography(&resolved, &mut own);
174 assert_eq!(own.letter_spacing, Some(Length::Px(18.0)));
175 }
176
177 #[test]
178 fn overlay_resolved_typography_leaves_a_field_alone_when_resolved_lacks_it() {
179 let resolved = CssStyle::default();
180 let mut own = CssStyle {
181 color: Some(Color::String("#00ff00".into())),
182 ..Default::default()
183 };
184 overlay_resolved_typography(&resolved, &mut own);
185 match own.color {
186 Some(Color::String(s)) => assert_eq!(s, "#00ff00"),
187 other => panic!("resolved had no color; own's must be left alone, got {other:?}"),
188 }
189 }
190}