Add constraint solver

GtkConstraintSolver is an implementation of the Cassowary constraint
solving algorithm:

  http://constraints.cs.washington.edu/cassowary/

The Cassowary method allows to incrementally solve a tableau of linear
equations, in the form of:

  x = y × coefficient + constant

with different weights, or strengths, applied to each one.

These equations can be used to describe constraints applied to a layout
of UI elements, which allows layout managers using the Cassowary method
to quickly, and efficiently, lay out widgets in complex relations
between themselves and their parent container.
This commit is contained in:
Emmanuele Bassi 2019-04-09 14:05:48 +01:00
parent 3b6ee32f83
commit 6b308cd71e
9 changed files with 4905 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,276 @@
/* gtkconstraintequationprivate.h: Constraint expressions and variables
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi
*/
#pragma once
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
GtkConstraintVariable *
gtk_constraint_variable_new (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_new_dummy (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_new_objective (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_new_slack (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_ref (GtkConstraintVariable *variable);
void
gtk_constraint_variable_unref (GtkConstraintVariable *variable);
void
gtk_constraint_variable_set_value (GtkConstraintVariable *variable,
double value);
double
gtk_constraint_variable_get_value (const GtkConstraintVariable *variable);
void
gtk_constraint_variable_set_prefix (GtkConstraintVariable *variable,
const char *prefix);
char *
gtk_constraint_variable_to_string (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_external (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_pivotable (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_restricted (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_dummy (const GtkConstraintVariable *variable);
typedef struct {
GtkConstraintVariable *first;
GtkConstraintVariable *second;
} GtkConstraintVariablePair;
GtkConstraintVariablePair *
gtk_constraint_variable_pair_new (GtkConstraintVariable *first,
GtkConstraintVariable *second);
void
gtk_constraint_variable_pair_free (GtkConstraintVariablePair *pair);
typedef struct _GtkConstraintVariableSet GtkConstraintVariableSet;
GtkConstraintVariableSet *
gtk_constraint_variable_set_new (void);
void
gtk_constraint_variable_set_free (GtkConstraintVariableSet *set);
gboolean
gtk_constraint_variable_set_add (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_set_remove (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable);
int
gtk_constraint_variable_set_size (GtkConstraintVariableSet *set);
typedef struct {
/*< private >*/
gpointer dummy1;
gpointer dummy2;
gint64 dummy3;
} GtkConstraintVariableSetIter;
void
gtk_constraint_variable_set_iter_init (GtkConstraintVariableSetIter *iter,
GtkConstraintVariableSet *set);
gboolean
gtk_constraint_variable_set_iter_next (GtkConstraintVariableSetIter *iter,
GtkConstraintVariable **variable_p);
GtkConstraintExpression *
gtk_constraint_expression_new (double constant);
GtkConstraintExpression *
gtk_constraint_expression_new_from_variable (GtkConstraintVariable *variable);
GtkConstraintExpression *
gtk_constraint_expression_ref (GtkConstraintExpression *expression);
void
gtk_constraint_expression_unref (GtkConstraintExpression *expression);
GtkConstraintExpression *
gtk_constraint_expression_clone (GtkConstraintExpression *expression);
void
gtk_constraint_expression_set_constant (GtkConstraintExpression *expression,
double constant);
double
gtk_constraint_expression_get_constant (const GtkConstraintExpression *expression);
gboolean
gtk_constraint_expression_is_constant (const GtkConstraintExpression *expression);
void
gtk_constraint_expression_add_expression (GtkConstraintExpression *a_expr,
GtkConstraintExpression *b_expr,
double n,
GtkConstraintVariable *subject,
GtkConstraintSolver *solver);
void
gtk_constraint_expression_add_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable,
double coefficient,
GtkConstraintVariable *subject,
GtkConstraintSolver *solver);
void
gtk_constraint_expression_remove_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
void
gtk_constraint_expression_set_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable,
double coefficient);
double
gtk_constraint_expression_get_coefficient (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
char *
gtk_constraint_expression_to_string (const GtkConstraintExpression *expression);
double
gtk_constraint_expression_new_subject (GtkConstraintExpression *expression,
GtkConstraintVariable *subject);
void
gtk_constraint_expression_change_subject (GtkConstraintExpression *expression,
GtkConstraintVariable *old_subject,
GtkConstraintVariable *new_subject);
void
gtk_constraint_expression_substitute_out (GtkConstraintExpression *expression,
GtkConstraintVariable *out_var,
GtkConstraintExpression *expr,
GtkConstraintVariable *subject,
GtkConstraintSolver *solver);
GtkConstraintVariable *
gtk_constraint_expression_get_pivotable_variable (GtkConstraintExpression *expression);
GtkConstraintExpression *
gtk_constraint_expression_plus_constant (GtkConstraintExpression *expression,
double constant);
GtkConstraintExpression *
gtk_constraint_expression_minus_constant (GtkConstraintExpression *expression,
double constant);
GtkConstraintExpression *
gtk_constraint_expression_plus_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
GtkConstraintExpression *
gtk_constraint_expression_minus_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
GtkConstraintExpression *
gtk_constraint_expression_multiply_by (GtkConstraintExpression *expression,
double factor);
GtkConstraintExpression *
gtk_constraint_expression_divide_by (GtkConstraintExpression *expression,
double factor);
struct _GtkConstraintExpressionBuilder
{
/*< private >*/
gpointer dummy1;
gpointer dummy2;
int dummy3;
};
void
gtk_constraint_expression_builder_init (GtkConstraintExpressionBuilder *builder,
GtkConstraintSolver *solver);
void
gtk_constraint_expression_builder_term (GtkConstraintExpressionBuilder *builder,
GtkConstraintVariable *term);
void
gtk_constraint_expression_builder_plus (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_minus (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_divide_by (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_multiply_by (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_constant (GtkConstraintExpressionBuilder *builder,
double value);
GtkConstraintExpression *
gtk_constraint_expression_builder_finish (GtkConstraintExpressionBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
/*< private >
* GtkConstraintExpressionIter:
*
* An iterator object for terms inside a #GtkConstraintExpression.
*/
typedef struct {
/*< private >*/
gpointer dummy1;
gpointer dummy2;
gint64 dummy3;
} GtkConstraintExpressionIter;
void
gtk_constraint_expression_iter_init (GtkConstraintExpressionIter *iter,
GtkConstraintExpression *equation);
gboolean
gtk_constraint_expression_iter_next (GtkConstraintExpressionIter *iter,
GtkConstraintVariable **variable,
double *coefficient);
gboolean
gtk_constraint_expression_iter_prev (GtkConstraintExpressionIter *iter,
GtkConstraintVariable **variable,
double *coefficient);
G_END_DECLS

2234
gtk/gtkconstraintsolver.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,114 @@
/* gtkconstraintsolverprivate.h: Constraint solver based on the Cassowary method
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi
*/
#pragma once
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
#define GTK_TYPE_CONSTRAINT_SOLVER (gtk_constraint_solver_get_type())
G_DECLARE_FINAL_TYPE (GtkConstraintSolver, gtk_constraint_solver, GTK, CONSTRAINT_SOLVER, GObject)
GtkConstraintSolver *
gtk_constraint_solver_new (void);
void
gtk_constraint_solver_freeze (GtkConstraintSolver *solver);
void
gtk_constraint_solver_thaw (GtkConstraintSolver *solver);
void
gtk_constraint_solver_resolve (GtkConstraintSolver *solver);
GtkConstraintVariable *
gtk_constraint_solver_create_variable (GtkConstraintSolver *solver,
const char *prefix,
const char *name,
double value);
GtkConstraintRef *
gtk_constraint_solver_add_constraint (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
GtkConstraintRelation relation,
GtkConstraintExpression *expression,
double strength);
void
gtk_constraint_solver_remove_constraint (GtkConstraintSolver *solver,
GtkConstraintRef *reference);
GtkConstraintRef *
gtk_constraint_solver_add_stay_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
double strength);
void
gtk_constraint_solver_remove_stay_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_solver_has_stay_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
GtkConstraintRef *
gtk_constraint_solver_add_edit_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
double strength);
void
gtk_constraint_solver_remove_edit_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_solver_has_edit_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
void
gtk_constraint_solver_suggest_value (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
double value);
void
gtk_constraint_solver_begin_edit (GtkConstraintSolver *solver);
void
gtk_constraint_solver_end_edit (GtkConstraintSolver *solver);
void
gtk_constraint_solver_note_added_variable (GtkConstraintSolver *self,
GtkConstraintVariable *variable,
GtkConstraintVariable *subject);
void
gtk_constraint_solver_note_removed_variable (GtkConstraintSolver *self,
GtkConstraintVariable *variable,
GtkConstraintVariable *subject);
void
gtk_constraint_solver_clear (GtkConstraintSolver *solver);
char *
gtk_constraint_solver_to_string (GtkConstraintSolver *solver);
G_END_DECLS

View File

@ -0,0 +1,50 @@
/* gtkconstrainttypesprivate.h: Private types for the constraint solver
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi
*/
#pragma once
#include <gtk/gtktypes.h>
#include <gtk/gtkenums.h>
G_BEGIN_DECLS
typedef struct _GtkConstraintVariable GtkConstraintVariable;
typedef struct _GtkConstraintExpression GtkConstraintExpression;
typedef struct _GtkConstraintExpressionBuilder GtkConstraintExpressionBuilder;
/*< private >
* GtkConstraintRef:
*
* A reference to a constraint stored inside the solver; while #GtkConstraint
* represent the public API, a #GtkConstraintRef represents data stored inside
* the solver. A #GtkConstraintRef is completely opaque, and should only be
* used to remove a constraint from the solver.
*/
typedef struct _GtkConstraintRef GtkConstraintRef;
/*< private >
* GtkConstraintSolver:
*
* A simplex solver using the Cassowary constraint solving algorithm.
*/
typedef struct _GtkConstraintSolver GtkConstraintSolver;
G_END_DECLS

View File

@ -1053,4 +1053,18 @@ typedef enum {
GTK_PICK_NON_TARGETABLE = 1 << 1
} GtkPickFlags;
/**
* GtkConstraintRelation:
* @GTK_CONSTRAINT_RELATION_EQ: Equal
* @GTK_CONSTRAINT_RELATION_LE: Less than, or equal
* @GTK_CONSTRAINT_RELATION_GE: Greater than, or equal
*
* The relation between two terms of a constraint.
*/
typedef enum {
GTK_CONSTRAINT_RELATION_LE = -1,
GTK_CONSTRAINT_RELATION_EQ = 0,
GTK_CONSTRAINT_RELATION_GE = 1
} GtkConstraintRelation;
#endif /* __GTK_ENUMS_H__ */

View File

@ -37,6 +37,8 @@ gtk_private_sources = files([
'gtkcolorpickershell.c',
'gtkcolorscale.c',
'gtkcolorswatch.c',
'gtkconstraintexpression.c',
'gtkconstraintsolver.c',
'gtkcssanimatedstyle.c',
'gtkcssanimation.c',
'gtkcssarrayvalue.c',

View File

@ -0,0 +1,368 @@
#include <locale.h>
#include "../../gtk/gtkconstrainttypesprivate.h"
#include "../../gtk/gtkconstraintsolverprivate.h"
#include "../../gtk/gtkconstraintexpressionprivate.h"
static void
constraint_solver_simple (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 167.0);
GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 2.0);
GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (y);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
double y_value = gtk_constraint_variable_get_value (y);
g_assert_cmpfloat_with_epsilon (x_value, y_value, 0.001);
g_assert_cmpfloat_with_epsilon (x_value, 0.0, 0.001);
g_assert_cmpfloat_with_epsilon (y_value, 0.0, 0.001);
gtk_constraint_variable_unref (y);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_stay (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 5.0);
GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 10.0);
gtk_constraint_solver_add_stay_variable (solver, x, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_stay_variable (solver, y, GTK_CONSTRAINT_WEIGHT_WEAK);
double x_value = gtk_constraint_variable_get_value (x);
double y_value = gtk_constraint_variable_get_value (y);
g_assert_cmpfloat_with_epsilon (x_value, 5.0, 0.001);
g_assert_cmpfloat_with_epsilon (y_value, 10.0, 0.001);
gtk_constraint_variable_unref (x);
gtk_constraint_variable_unref (y);
g_object_unref (solver);
}
static void
constraint_solver_variable_geq_constant (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
GtkConstraintExpression *e = gtk_constraint_expression_new (100.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_GE, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
g_assert_cmpfloat (x_value, >=, 100.0);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_variable_leq_constant (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 100.0);
GtkConstraintExpression *e = gtk_constraint_expression_new (10.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_LE, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
g_assert_cmpfloat (x_value, <=, 10.0);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_variable_eq_constant (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
GtkConstraintExpression *e = gtk_constraint_expression_new (100.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
g_assert_cmpfloat_with_epsilon (x_value, 100.0, 0.001);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_eq_with_stay (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
GtkConstraintVariable *width = gtk_constraint_solver_create_variable (solver, NULL, "width", 10.0);
GtkConstraintVariable *right_min = gtk_constraint_solver_create_variable (solver, NULL, "rightMin", 100.0);
GtkConstraintExpressionBuilder builder;
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, x);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_term (&builder, width);
GtkConstraintExpression *right = gtk_constraint_expression_builder_finish (&builder);
gtk_constraint_solver_add_stay_variable (solver, width, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_stay_variable (solver, right_min, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_constraint (solver,
right_min, GTK_CONSTRAINT_RELATION_EQ, right,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
double width_value = gtk_constraint_variable_get_value (width);
g_assert_cmpfloat_with_epsilon (x_value, 90.0, 0.001);
g_assert_cmpfloat_with_epsilon (width_value, 10.0, 0.001);
gtk_constraint_variable_unref (right_min);
gtk_constraint_variable_unref (width);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_cassowary (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 0.0);
GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 0.0);
GtkConstraintExpression *e;
e = gtk_constraint_expression_new_from_variable (y);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_LE, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
e = gtk_constraint_expression_plus_constant (gtk_constraint_expression_new_from_variable (x), 3.0);
gtk_constraint_solver_add_constraint (solver,
y, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
e = gtk_constraint_expression_new (10.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_WEAK);
e = gtk_constraint_expression_new (10.0);
gtk_constraint_solver_add_constraint (solver,
y, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_WEAK);
double x_val = gtk_constraint_variable_get_value (x);
double y_val = gtk_constraint_variable_get_value (y);
g_test_message ("x = %g, y = %g", x_val, y_val);
/* The system is unstable and has two possible solutions we need to test */
g_assert_true ((G_APPROX_VALUE (x_val, 10.0, 1e-8) &&
G_APPROX_VALUE (y_val, 13.0, 1e-8)) ||
(G_APPROX_VALUE (x_val, 7.0, 1e-8) &&
G_APPROX_VALUE (y_val, 10.0, 1e-8)));
gtk_constraint_variable_unref (x);
gtk_constraint_variable_unref (y);
g_object_unref (solver);
}
static void
constraint_solver_edit_var_required (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, "a", 0.0);
gtk_constraint_solver_add_stay_variable (solver, a, GTK_CONSTRAINT_WEIGHT_STRONG);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001);
gtk_constraint_solver_add_edit_variable (solver, a, GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_begin_edit (solver);
gtk_constraint_solver_suggest_value (solver, a, 2.0);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001);
gtk_constraint_solver_suggest_value (solver, a, 10.0);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001);
gtk_constraint_solver_end_edit (solver);
gtk_constraint_variable_unref (a);
g_object_unref (solver);
}
static void
constraint_solver_edit_var_suggest (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, "a", 0.0);
GtkConstraintVariable *b = gtk_constraint_solver_create_variable (solver, NULL, "b", 0.0);
gtk_constraint_solver_add_stay_variable (solver, a, GTK_CONSTRAINT_WEIGHT_STRONG);
GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (b);
gtk_constraint_solver_add_constraint (solver,
a, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 0.0, 0.001);
gtk_constraint_solver_add_edit_variable (solver, a, GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_begin_edit (solver);
gtk_constraint_solver_suggest_value (solver, a, 2.0);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 2.0, 0.001);
gtk_constraint_solver_suggest_value (solver, a, 10.0);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 10.0, 0.001);
gtk_constraint_variable_unref (a);
gtk_constraint_variable_unref (b);
g_object_unref (solver);
}
static void
constraint_solver_paper (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *left = gtk_constraint_solver_create_variable (solver, NULL, "left", 0.0);
GtkConstraintVariable *middle = gtk_constraint_solver_create_variable (solver, NULL, "middle", 0.0);
GtkConstraintVariable *right = gtk_constraint_solver_create_variable (solver, NULL, "right", 0.0);
GtkConstraintExpressionBuilder builder;
GtkConstraintExpression *expr;
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, left);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_term (&builder, right);
gtk_constraint_expression_builder_divide_by (&builder);
gtk_constraint_expression_builder_constant (&builder, 2.0);
expr = gtk_constraint_expression_builder_finish (&builder);
gtk_constraint_solver_add_constraint (solver,
middle, GTK_CONSTRAINT_RELATION_EQ, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, left);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_constant (&builder, 10.0);
expr = gtk_constraint_expression_builder_finish (&builder);
gtk_constraint_solver_add_constraint (solver,
right, GTK_CONSTRAINT_RELATION_EQ, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
expr = gtk_constraint_expression_new (100.0);
gtk_constraint_solver_add_constraint (solver,
right, GTK_CONSTRAINT_RELATION_LE, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
expr = gtk_constraint_expression_new (0.0);
gtk_constraint_solver_add_constraint (solver,
left, GTK_CONSTRAINT_RELATION_GE, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_test_message ("Check constraints hold");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle),
(gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0,
0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right),
gtk_constraint_variable_get_value (left) + 10.0,
0.001);
g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0);
g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0);
gtk_constraint_variable_set_value (middle, 45.0);
gtk_constraint_solver_add_stay_variable (solver, middle, GTK_CONSTRAINT_WEIGHT_WEAK);
g_test_message ("Check constraints hold after setting middle");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle),
(gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0,
0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right),
gtk_constraint_variable_get_value (left) + 10.0,
0.001);
g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0);
g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (left), 40.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle), 45.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right), 50.0, 0.001);
gtk_constraint_variable_unref (left);
gtk_constraint_variable_unref (middle);
gtk_constraint_variable_unref (right);
g_object_unref (solver);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
setlocale (LC_ALL, "C");
g_test_add_func ("/constraint-solver/paper", constraint_solver_paper);
g_test_add_func ("/constraint-solver/simple", constraint_solver_simple);
g_test_add_func ("/constraint-solver/constant/eq", constraint_solver_variable_eq_constant);
g_test_add_func ("/constraint-solver/constant/ge", constraint_solver_variable_geq_constant);
g_test_add_func ("/constraint-solver/constant/le", constraint_solver_variable_leq_constant);
g_test_add_func ("/constraint-solver/stay/simple", constraint_solver_stay);
g_test_add_func ("/constraint-solver/stay/eq", constraint_solver_eq_with_stay);
g_test_add_func ("/constraint-solver/cassowary", constraint_solver_cassowary);
g_test_add_func ("/constraint-solver/edit/required", constraint_solver_edit_var_required);
g_test_add_func ("/constraint-solver/edit/suggest", constraint_solver_edit_var_suggest);
return g_test_run ();
}

View File

@ -17,6 +17,11 @@ tests = [
['builderparser'],
['cellarea'],
['check-icon-names'],
['constraint-solver', [
'../../gtk/gtkconstraintsolver.c',
'../../gtk/gtkconstraintexpression.c',
], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']
],
['cssprovider'],
['rbtree-crash', ['../../gtk/gtkrbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
['defaultvalue'],