1 | /* |
2 | * Copyright (C) 2021 Red Hat Inc. |
3 | * |
4 | * Author: |
5 | * Matthias Clasen <mclasen@redhat.com> |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public |
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <gtk/gtk.h> |
24 | #include "gtk/gtkcssvalueprivate.h" |
25 | #include "gtk/gtkcsscolorvalueprivate.h" |
26 | #include "gtk/gtkcssnumbervalueprivate.h" |
27 | #include "gtk/gtkcssstylepropertyprivate.h" |
28 | #include "gtk/gtkcssstaticstyleprivate.h" |
29 | |
30 | static gboolean |
31 | color_is_near (const GdkRGBA *color1, |
32 | const GdkRGBA *color2) |
33 | { |
34 | if (fabs (x: color1->red - color2->red) > FLT_EPSILON) |
35 | return FALSE; |
36 | if (fabs (x: color1->green - color2->green) > FLT_EPSILON) |
37 | return FALSE; |
38 | if (fabs (x: color1->blue- color2->blue) > FLT_EPSILON) |
39 | return FALSE; |
40 | if (fabs (x: color1->alpha- color2->alpha) > FLT_EPSILON) |
41 | return FALSE; |
42 | return TRUE; |
43 | } |
44 | |
45 | static gboolean |
46 | value_is_near (int prop, |
47 | GtkCssValue *value1, |
48 | GtkCssValue *value2) |
49 | { |
50 | if (_gtk_css_value_equal (value1, value2)) |
51 | return TRUE; |
52 | |
53 | switch (prop) |
54 | { |
55 | case GTK_CSS_PROPERTY_COLOR: |
56 | { |
57 | const GdkRGBA *c1, *c2; |
58 | gboolean res; |
59 | |
60 | c1 = gtk_css_color_value_get_rgba (color: value1); |
61 | c2 = gtk_css_color_value_get_rgba (color: value2); |
62 | |
63 | res = color_is_near (color1: c1, color2: c2); |
64 | |
65 | return res; |
66 | } |
67 | break; |
68 | |
69 | case GTK_CSS_PROPERTY_FONT_SIZE: |
70 | { |
71 | double n1, n2; |
72 | |
73 | n1 = _gtk_css_number_value_get (number: value1, one_hundred_percent: 100); |
74 | n2 = _gtk_css_number_value_get (number: value2, one_hundred_percent: 100); |
75 | |
76 | return fabs (x: n1 - n2) < FLT_EPSILON; |
77 | } |
78 | break; |
79 | |
80 | default: |
81 | break; |
82 | } |
83 | |
84 | return FALSE; |
85 | } |
86 | |
87 | static void |
88 | assert_css_value (int prop, |
89 | GtkCssValue *result, |
90 | GtkCssValue *expected) |
91 | { |
92 | if (result == expected) |
93 | return; |
94 | |
95 | if (((result == NULL) != (expected == NULL)) || |
96 | !value_is_near (prop, value1: result, value2: expected)) |
97 | { |
98 | char *r = result ? _gtk_css_value_to_string (value: result) : g_strdup (str: "(nil)" ); |
99 | char *e = expected ? _gtk_css_value_to_string (value: expected) : g_strdup (str: "(nil)" ); |
100 | g_print (format: "Expected %s got %s\n" , e, r); |
101 | g_free (mem: r); |
102 | g_free (mem: e); |
103 | |
104 | g_assert_not_reached (); |
105 | } |
106 | } |
107 | |
108 | /* Tests for css transitions */ |
109 | |
110 | typedef struct { |
111 | int prop; |
112 | const char *value1; |
113 | const char *value2; |
114 | double progress; |
115 | const char *value3; |
116 | } ValueTransitionTest; |
117 | |
118 | static ValueTransitionTest tests[] = { |
119 | { GTK_CSS_PROPERTY_COLOR, "transparent" , "rgb(255,0,0)" , 0.25, "rgba(255,0,0,0.25)" }, |
120 | { GTK_CSS_PROPERTY_BOX_SHADOW, "none" , "2px 2px 10px 4px rgb(200,200,200)" , 0.5, "1px 1px 5px 2px rgba(200,200,200,0.5)" }, |
121 | { GTK_CSS_PROPERTY_BOX_SHADOW, "2px 2px 10px 4px rgb(200,200,200)" , "none" , 0.5, "1px 1px 5px 2px rgba(200,200,200,0.5)" }, |
122 | { GTK_CSS_PROPERTY_BOX_SHADOW, "2px 2px 10px 4px rgb(200,200,200), 0px 10px 8px 6px rgb(200,100,0)" , "none" , 0.5, "1px 1px 5px 2px rgba(200,200,200,0.5), 0px 5px 4px 3px rgba(200,100,0,0.5)" }, |
123 | { GTK_CSS_PROPERTY_FONT_SIZE, "12px" , "16px" , 0.25, "13px" }, |
124 | { GTK_CSS_PROPERTY_FONT_SIZE, "10px" , "10pt" , 0.5, "11.66666667px" }, |
125 | { GTK_CSS_PROPERTY_FONT_FAMILY, "cantarell" , "sans" , 0, "cantarell" }, |
126 | { GTK_CSS_PROPERTY_FONT_FAMILY, "cantarell" , "sans" , 1, "sans" }, |
127 | { GTK_CSS_PROPERTY_FONT_FAMILY, "cantarell" , "sans" , 0.5, NULL }, |
128 | { GTK_CSS_PROPERTY_BACKGROUND_POSITION, "20px 10px" , "40px" , 0.5, "30px calc(5px + 25%)" }, |
129 | //TODO We don't currently transition border-image-width |
130 | //{ GTK_CSS_PROPERTY_BORDER_IMAGE_WIDTH, "10px 20px", "0px", 0.5, "5px 10px 0.5px 0.5px" }, |
131 | { GTK_CSS_PROPERTY_FILTER, "none" , "blur(6px)" , 0.5, "blur(3px)" }, |
132 | { GTK_CSS_PROPERTY_FILTER, "none" , "blur(6px),contrast(0.6)" , 0.5, "blur(3px),contrast(0.3)" }, |
133 | { GTK_CSS_PROPERTY_FILTER, "contrast(0.6)" , "blur(6px)" , 0.5, NULL}, |
134 | }; |
135 | |
136 | static GtkCssValue * |
137 | value_from_string (GtkStyleProperty *prop, |
138 | const char *str) |
139 | { |
140 | GBytes *bytes; |
141 | GtkCssParser *parser; |
142 | GtkCssValue *value; |
143 | |
144 | bytes = g_bytes_new_static (data: str, size: strlen (s: str)); |
145 | parser = gtk_css_parser_new_for_bytes (bytes, NULL, NULL, NULL, NULL); |
146 | value = _gtk_style_property_parse_value (property: prop, parser); |
147 | gtk_css_parser_unref (self: parser); |
148 | g_bytes_unref (bytes); |
149 | |
150 | return value; |
151 | } |
152 | |
153 | static void |
154 | test_transition (gconstpointer data) |
155 | { |
156 | ValueTransitionTest *test = &tests[GPOINTER_TO_INT (data)]; |
157 | GtkStyleProperty *prop; |
158 | GtkCssValue *value1; |
159 | GtkCssValue *value2; |
160 | GtkCssValue *value3; |
161 | GtkCssValue *computed1; |
162 | GtkCssValue *computed2; |
163 | GtkCssValue *computed3; |
164 | GtkCssValue *result; |
165 | GtkStyleProvider *provider; |
166 | GtkCssStyle *style; |
167 | |
168 | provider = GTK_STYLE_PROVIDER (gtk_settings_get_default ()); |
169 | style = gtk_css_static_style_get_default (); |
170 | |
171 | prop = (GtkStyleProperty *)_gtk_css_style_property_lookup_by_id (id: test->prop); |
172 | |
173 | value1 = value_from_string (prop, str: test->value1); |
174 | g_assert_nonnull (value1); |
175 | computed1 = _gtk_css_value_compute (value: value1, property_id: test->prop, provider, style, NULL); |
176 | |
177 | value2 = value_from_string (prop, str: test->value2); |
178 | g_assert_nonnull (value1); |
179 | computed2 = _gtk_css_value_compute (value: value2, property_id: test->prop, provider, style, NULL); |
180 | |
181 | if (test->value3) |
182 | { |
183 | value3 = value_from_string (prop, str: test->value3); |
184 | computed3 = _gtk_css_value_compute (value: value3, property_id: test->prop, provider, style, NULL); |
185 | } |
186 | else |
187 | { |
188 | value3 = computed3 = NULL; |
189 | } |
190 | |
191 | result = _gtk_css_value_transition (start: computed1, end: computed2, property_id: test->prop, progress: test->progress); |
192 | assert_css_value (prop: test->prop, result, expected: computed3); |
193 | |
194 | gtk_css_value_unref (value: value1); |
195 | gtk_css_value_unref (value: value2); |
196 | if (value3) |
197 | gtk_css_value_unref (value: value3); |
198 | gtk_css_value_unref (value: computed1); |
199 | gtk_css_value_unref (value: computed2); |
200 | if (computed3) |
201 | gtk_css_value_unref (value: computed3); |
202 | gtk_css_value_unref (value: result); |
203 | } |
204 | |
205 | int |
206 | main (int argc, char **argv) |
207 | { |
208 | GtkStyleProperty *previous; |
209 | int j; |
210 | |
211 | gtk_test_init (argcp: &argc, argvp: &argv); |
212 | |
213 | previous = NULL; |
214 | j = 0; |
215 | for (int i = 0; i < G_N_ELEMENTS (tests); i++) |
216 | { |
217 | ValueTransitionTest *test = &tests[i]; |
218 | GtkStyleProperty *prop; |
219 | char *path; |
220 | |
221 | prop = (GtkStyleProperty *)_gtk_css_style_property_lookup_by_id (id: test->prop); |
222 | if (prop != previous) |
223 | { |
224 | previous = prop; |
225 | j = 0; |
226 | } |
227 | else |
228 | j++; |
229 | |
230 | path = g_strdup_printf (format: "/css/value/transition/%s/%d" , _gtk_style_property_get_name (property: (GtkStyleProperty *)prop), j); |
231 | g_test_add_data_func (testpath: path, GINT_TO_POINTER (i), test_func: test_transition); |
232 | g_free (mem: path); |
233 | } |
234 | |
235 | return g_test_run (); |
236 | } |
237 | |