1 #include <locale.h>
2 
3 #include <gtk/gtk.h>
4 #include "../../gtk/gtkconstrainttypesprivate.h"
5 #include "../../gtk/gtkconstraintsolverprivate.h"
6 #include "../../gtk/gtkconstraintexpressionprivate.h"
7 
8 static void
constraint_solver_simple(void)9 constraint_solver_simple (void)
10 {
11   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
12 
13   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 167.0);
14   GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 2.0);
15 
16   GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (y);
17 
18   gtk_constraint_solver_add_constraint (solver,
19                                         x, GTK_CONSTRAINT_RELATION_EQ, e,
20                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
21 
22   double x_value = gtk_constraint_variable_get_value (x);
23   double y_value = gtk_constraint_variable_get_value (y);
24 
25   g_assert_cmpfloat_with_epsilon (x_value, y_value, 0.001);
26   g_assert_cmpfloat_with_epsilon (x_value, 0.0, 0.001);
27   g_assert_cmpfloat_with_epsilon (y_value, 0.0, 0.001);
28 
29   gtk_constraint_variable_unref (y);
30   gtk_constraint_variable_unref (x);
31 
32   g_object_unref (solver);
33 }
34 
35 static void
constraint_solver_stay(void)36 constraint_solver_stay (void)
37 {
38   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
39 
40   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 5.0);
41   GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 10.0);
42 
43   gtk_constraint_solver_add_stay_variable (solver, x, GTK_CONSTRAINT_STRENGTH_WEAK);
44   gtk_constraint_solver_add_stay_variable (solver, y, GTK_CONSTRAINT_STRENGTH_WEAK);
45 
46   double x_value = gtk_constraint_variable_get_value (x);
47   double y_value = gtk_constraint_variable_get_value (y);
48 
49   g_assert_cmpfloat_with_epsilon (x_value, 5.0, 0.001);
50   g_assert_cmpfloat_with_epsilon (y_value, 10.0, 0.001);
51 
52   gtk_constraint_variable_unref (x);
53   gtk_constraint_variable_unref (y);
54 
55   g_object_unref (solver);
56 }
57 
58 static void
constraint_solver_variable_geq_constant(void)59 constraint_solver_variable_geq_constant (void)
60 {
61   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
62 
63   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
64   GtkConstraintExpression *e = gtk_constraint_expression_new (100.0);
65 
66   gtk_constraint_solver_add_constraint (solver,
67                                         x, GTK_CONSTRAINT_RELATION_GE, e,
68                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
69 
70   double x_value = gtk_constraint_variable_get_value (x);
71 
72   g_assert_cmpfloat (x_value, >=, 100.0);
73 
74   gtk_constraint_variable_unref (x);
75 
76   g_object_unref (solver);
77 }
78 
79 static void
constraint_solver_variable_leq_constant(void)80 constraint_solver_variable_leq_constant (void)
81 {
82   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
83 
84   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 100.0);
85   GtkConstraintExpression *e = gtk_constraint_expression_new (10.0);
86 
87   gtk_constraint_solver_add_constraint (solver,
88                                         x, GTK_CONSTRAINT_RELATION_LE, e,
89                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
90 
91   double x_value = gtk_constraint_variable_get_value (x);
92 
93   g_assert_cmpfloat (x_value, <=, 10.0);
94 
95   gtk_constraint_variable_unref (x);
96 
97   g_object_unref (solver);
98 }
99 
100 static void
constraint_solver_variable_eq_constant(void)101 constraint_solver_variable_eq_constant (void)
102 {
103   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
104 
105   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
106   GtkConstraintExpression *e = gtk_constraint_expression_new (100.0);
107 
108   gtk_constraint_solver_add_constraint (solver,
109                                         x, GTK_CONSTRAINT_RELATION_EQ, e,
110                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
111 
112   double x_value = gtk_constraint_variable_get_value (x);
113 
114   g_assert_cmpfloat_with_epsilon (x_value, 100.0, 0.001);
115 
116   gtk_constraint_variable_unref (x);
117 
118   g_object_unref (solver);
119 }
120 
121 static void
constraint_solver_eq_with_stay(void)122 constraint_solver_eq_with_stay (void)
123 {
124   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
125 
126   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
127   GtkConstraintVariable *width = gtk_constraint_solver_create_variable (solver, NULL, "width", 10.0);
128   GtkConstraintVariable *right_min = gtk_constraint_solver_create_variable (solver, NULL, "rightMin", 100.0);
129 
130   GtkConstraintExpressionBuilder builder;
131   gtk_constraint_expression_builder_init (&builder, solver);
132   gtk_constraint_expression_builder_term (&builder, x);
133   gtk_constraint_expression_builder_plus (&builder);
134   gtk_constraint_expression_builder_term (&builder, width);
135   GtkConstraintExpression *right = gtk_constraint_expression_builder_finish (&builder);
136 
137   gtk_constraint_solver_add_stay_variable (solver, width, GTK_CONSTRAINT_STRENGTH_WEAK);
138   gtk_constraint_solver_add_stay_variable (solver, right_min, GTK_CONSTRAINT_STRENGTH_WEAK);
139   gtk_constraint_solver_add_constraint (solver,
140                                         right_min, GTK_CONSTRAINT_RELATION_EQ, right,
141                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
142 
143   double x_value = gtk_constraint_variable_get_value (x);
144   double width_value = gtk_constraint_variable_get_value (width);
145 
146   g_assert_cmpfloat_with_epsilon (x_value, 90.0, 0.001);
147   g_assert_cmpfloat_with_epsilon (width_value, 10.0, 0.001);
148 
149   gtk_constraint_variable_unref (right_min);
150   gtk_constraint_variable_unref (width);
151   gtk_constraint_variable_unref (x);
152 
153   g_object_unref (solver);
154 }
155 
156 static void
constraint_solver_cassowary(void)157 constraint_solver_cassowary (void)
158 {
159   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
160 
161   GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 0.0);
162   GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 0.0);
163 
164   GtkConstraintExpression *e;
165 
166   e = gtk_constraint_expression_new_from_variable (y);
167   gtk_constraint_solver_add_constraint (solver,
168                                         x, GTK_CONSTRAINT_RELATION_LE, e,
169                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
170 
171   e = gtk_constraint_expression_plus_constant (gtk_constraint_expression_new_from_variable (x), 3.0);
172   gtk_constraint_solver_add_constraint (solver,
173                                         y, GTK_CONSTRAINT_RELATION_EQ, e,
174                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
175 
176   e = gtk_constraint_expression_new (10.0);
177   gtk_constraint_solver_add_constraint (solver,
178                                         x, GTK_CONSTRAINT_RELATION_EQ, e,
179                                         GTK_CONSTRAINT_STRENGTH_WEAK);
180 
181   e = gtk_constraint_expression_new (10.0);
182   gtk_constraint_solver_add_constraint (solver,
183                                         y, GTK_CONSTRAINT_RELATION_EQ, e,
184                                         GTK_CONSTRAINT_STRENGTH_WEAK);
185 
186   double x_val = gtk_constraint_variable_get_value (x);
187   double y_val = gtk_constraint_variable_get_value (y);
188 
189   g_test_message ("x = %g, y = %g", x_val, y_val);
190 
191   /* The system is unstable and has two possible solutions we need to test */
192   g_assert_true ((G_APPROX_VALUE (x_val, 10.0, 1e-8) &&
193                   G_APPROX_VALUE (y_val, 13.0, 1e-8)) ||
194                  (G_APPROX_VALUE (x_val,  7.0, 1e-8) &&
195                   G_APPROX_VALUE (y_val, 10.0, 1e-8)));
196 
197   gtk_constraint_variable_unref (x);
198   gtk_constraint_variable_unref (y);
199 
200   g_object_unref (solver);
201 }
202 
203 static void
constraint_solver_edit_var_required(void)204 constraint_solver_edit_var_required (void)
205 {
206   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
207 
208   GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, "a", 0.0);
209   gtk_constraint_solver_add_stay_variable (solver, a, GTK_CONSTRAINT_STRENGTH_STRONG);
210 
211   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001);
212 
213   gtk_constraint_solver_add_edit_variable (solver, a, GTK_CONSTRAINT_STRENGTH_REQUIRED);
214   gtk_constraint_solver_begin_edit (solver);
215   gtk_constraint_solver_suggest_value (solver, a, 2.0);
216   gtk_constraint_solver_resolve (solver);
217 
218   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001);
219 
220   gtk_constraint_solver_suggest_value (solver, a, 10.0);
221   gtk_constraint_solver_resolve (solver);
222 
223   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001);
224 
225   gtk_constraint_solver_end_edit (solver);
226 
227   gtk_constraint_variable_unref (a);
228 
229   g_object_unref (solver);
230 }
231 
232 static void
constraint_solver_edit_var_suggest(void)233 constraint_solver_edit_var_suggest (void)
234 {
235   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
236 
237   GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, "a", 0.0);
238   GtkConstraintVariable *b = gtk_constraint_solver_create_variable (solver, NULL, "b", 0.0);
239 
240   gtk_constraint_solver_add_stay_variable (solver, a, GTK_CONSTRAINT_STRENGTH_STRONG);
241 
242   GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (b);
243   gtk_constraint_solver_add_constraint (solver,
244                                         a, GTK_CONSTRAINT_RELATION_EQ, e,
245                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
246 
247   gtk_constraint_solver_resolve (solver);
248 
249   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001);
250   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 0.0, 0.001);
251 
252   gtk_constraint_solver_add_edit_variable (solver, a, GTK_CONSTRAINT_STRENGTH_REQUIRED);
253   gtk_constraint_solver_begin_edit (solver);
254 
255   gtk_constraint_solver_suggest_value (solver, a, 2.0);
256   gtk_constraint_solver_resolve (solver);
257 
258   g_test_message ("Check values after first edit");
259 
260   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001);
261   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 2.0, 0.001);
262 
263   gtk_constraint_solver_suggest_value (solver, a, 10.0);
264   gtk_constraint_solver_resolve (solver);
265 
266   g_test_message ("Check values after second edit");
267 
268   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001);
269   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 10.0, 0.001);
270 
271   gtk_constraint_solver_suggest_value (solver, a, 12.0);
272   gtk_constraint_solver_resolve (solver);
273 
274   g_test_message ("Check values after third edit");
275 
276   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 12.0, 0.001);
277   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 12.0, 0.001);
278 
279   gtk_constraint_variable_unref (a);
280   gtk_constraint_variable_unref (b);
281 
282   g_object_unref (solver);
283 }
284 
285 static void
constraint_solver_paper(void)286 constraint_solver_paper (void)
287 {
288   GtkConstraintSolver *solver = gtk_constraint_solver_new ();
289 
290   GtkConstraintVariable *left = gtk_constraint_solver_create_variable (solver, NULL, "left", 0.0);
291   GtkConstraintVariable *middle = gtk_constraint_solver_create_variable (solver, NULL, "middle", 0.0);
292   GtkConstraintVariable *right = gtk_constraint_solver_create_variable (solver, NULL, "right", 0.0);
293 
294   GtkConstraintExpressionBuilder builder;
295   GtkConstraintExpression *expr;
296 
297   gtk_constraint_expression_builder_init (&builder, solver);
298   gtk_constraint_expression_builder_term (&builder, left);
299   gtk_constraint_expression_builder_plus (&builder);
300   gtk_constraint_expression_builder_term (&builder, right);
301   gtk_constraint_expression_builder_divide_by (&builder);
302   gtk_constraint_expression_builder_constant (&builder, 2.0);
303   expr = gtk_constraint_expression_builder_finish (&builder);
304   gtk_constraint_solver_add_constraint (solver,
305                                         middle, GTK_CONSTRAINT_RELATION_EQ, expr,
306                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
307 
308   gtk_constraint_expression_builder_init (&builder, solver);
309   gtk_constraint_expression_builder_term (&builder, left);
310   gtk_constraint_expression_builder_plus (&builder);
311   gtk_constraint_expression_builder_constant (&builder, 10.0);
312   expr = gtk_constraint_expression_builder_finish (&builder);
313   gtk_constraint_solver_add_constraint (solver,
314                                         right, GTK_CONSTRAINT_RELATION_EQ, expr,
315                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
316 
317   expr = gtk_constraint_expression_new (100.0);
318   gtk_constraint_solver_add_constraint (solver,
319                                         right, GTK_CONSTRAINT_RELATION_LE, expr,
320                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
321 
322   expr = gtk_constraint_expression_new (0.0);
323   gtk_constraint_solver_add_constraint (solver,
324                                         left, GTK_CONSTRAINT_RELATION_GE, expr,
325                                         GTK_CONSTRAINT_STRENGTH_REQUIRED);
326 
327   g_test_message ("Check constraints hold");
328 
329   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle),
330                                   (gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0,
331                                   0.001);
332   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right),
333                                   gtk_constraint_variable_get_value (left) + 10.0,
334                                   0.001);
335   g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0);
336   g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0);
337 
338   gtk_constraint_variable_set_value (middle, 45.0);
339   gtk_constraint_solver_add_stay_variable (solver, middle, GTK_CONSTRAINT_STRENGTH_WEAK);
340 
341   g_test_message ("Check constraints hold after setting middle");
342 
343   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle),
344                                   (gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0,
345                                   0.001);
346   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right),
347                                   gtk_constraint_variable_get_value (left) + 10.0,
348                                   0.001);
349   g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0);
350   g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0);
351 
352   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (left), 40.0, 0.001);
353   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle), 45.0, 0.001);
354   g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right), 50.0, 0.001);
355 
356   gtk_constraint_variable_unref (left);
357   gtk_constraint_variable_unref (middle);
358   gtk_constraint_variable_unref (right);
359 
360   g_object_unref (solver);
361 }
362 
363 int
main(int argc,char * argv[])364 main (int argc, char *argv[])
365 {
366   (g_test_init) (&argc, &argv, NULL);
367   setlocale (LC_ALL, "C");
368 
369   g_test_add_func ("/constraint-solver/paper", constraint_solver_paper);
370   g_test_add_func ("/constraint-solver/simple", constraint_solver_simple);
371   g_test_add_func ("/constraint-solver/constant/eq", constraint_solver_variable_eq_constant);
372   g_test_add_func ("/constraint-solver/constant/ge", constraint_solver_variable_geq_constant);
373   g_test_add_func ("/constraint-solver/constant/le", constraint_solver_variable_leq_constant);
374   g_test_add_func ("/constraint-solver/stay/simple", constraint_solver_stay);
375   g_test_add_func ("/constraint-solver/stay/eq", constraint_solver_eq_with_stay);
376   g_test_add_func ("/constraint-solver/cassowary", constraint_solver_cassowary);
377   g_test_add_func ("/constraint-solver/edit/required", constraint_solver_edit_var_required);
378   g_test_add_func ("/constraint-solver/edit/suggest", constraint_solver_edit_var_suggest);
379 
380   return g_test_run ();
381 }
382