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
30static gboolean
31color_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
45static gboolean
46value_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
87static void
88assert_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
110typedef struct {
111 int prop;
112 const char *value1;
113 const char *value2;
114 double progress;
115 const char *value3;
116} ValueTransitionTest;
117
118static 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
136static GtkCssValue *
137value_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
153static void
154test_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
205int
206main (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

source code of gtk/testsuite/css/transition.c