summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2019-06-25 19:38:32 -0400
committerEmmanuele Bassi <ebassi@gnome.org>2019-06-30 23:42:44 +0100
commit38d353dc1a0cb725681bb2c6d108c5ca77db23d7 (patch)
treef4689b2a5b8f48eccafff3f6dcbf8ac00c971a83
parent7ae04ba36b3031a8795e2778e171c886f6b922ce (diff)
downloadgtk+-38d353dc1a0cb725681bb2c6d108c5ca77db23d7.tar.gz
Add GtkConstraintGuide
This is meant to be a flexible space.
-rw-r--r--demos/gtk-demo/constraints.c21
-rw-r--r--gtk/gtkconstraint.c2
-rw-r--r--gtk/gtkconstraintlayout.c585
-rw-r--r--gtk/gtkconstraintlayout.h35
4 files changed, 577 insertions, 66 deletions
diff --git a/demos/gtk-demo/constraints.c b/demos/gtk-demo/constraints.c
index cc6cac145e..ba13f9b00f 100644
--- a/demos/gtk-demo/constraints.c
+++ b/demos/gtk-demo/constraints.c
@@ -74,6 +74,16 @@ static void
build_constraints (SimpleGrid *self,
GtkConstraintLayout *manager)
{
+ GtkConstraintGuide *guide;
+
+ guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE,
+ "min-width", 10,
+ "min-height", 10,
+ "nat-width", 100,
+ "nat-height", 10,
+ NULL);
+ gtk_constraint_layout_add_guide (manager, guide);
+
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
@@ -96,10 +106,19 @@ build_constraints (SimpleGrid *self,
gtk_constraint_new (self->button1,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
+ guide,
+ GTK_CONSTRAINT_ATTRIBUTE_START,
+ 1.0,
+ 0.0,
+ GTK_CONSTRAINT_STRENGTH_REQUIRED));
+ gtk_constraint_layout_add_constraint (manager,
+ gtk_constraint_new (guide,
+ GTK_CONSTRAINT_ATTRIBUTE_END,
+ GTK_CONSTRAINT_RELATION_EQ,
self->button2,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
- -12.0,
+ 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button2,
diff --git a/gtk/gtkconstraint.c b/gtk/gtkconstraint.c
index 6c20c48b94..2ac4d5b05d 100644
--- a/gtk/gtkconstraint.c
+++ b/gtk/gtkconstraint.c
@@ -572,7 +572,7 @@ gtk_constraint_is_constant (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
- return constraint->source_widget == NULL &&
+ return constraint->source == NULL &&
constraint->source_attribute == GTK_CONSTRAINT_ATTRIBUTE_NONE;
}
diff --git a/gtk/gtkconstraintlayout.c b/gtk/gtkconstraintlayout.c
index b5375c596a..405d080164 100644
--- a/gtk/gtkconstraintlayout.c
+++ b/gtk/gtkconstraintlayout.c
@@ -72,16 +72,38 @@
#include "gtksizerequest.h"
#include "gtkwidgetprivate.h"
-struct _GtkConstraintLayoutChild
+typedef struct
{
- GtkLayoutChild parent_instance;
-
/* HashTable<static string, Variable>; a hash table of variables,
* one for each attribute; we use these to query and suggest the
* values for the solver. The string is static and does not need
* to be freed.
*/
GHashTable *bound_attributes;
+} ConstraintSolverChildData;
+
+struct _GtkConstraintLayoutChild
+{
+ GtkLayoutChild parent_instance;
+
+ ConstraintSolverChildData data;
+};
+
+struct _GtkConstraintGuide
+{
+ GObject parent_instance;
+
+ int min_width;
+ int min_height;
+ int nat_width;
+ int nat_height;
+
+ GtkConstraintLayout *layout;
+
+ ConstraintSolverChildData data;
+
+ GtkConstraintRef *width_constraint[2];
+ GtkConstraintRef *height_constraint[2];
};
struct _GtkConstraintLayout
@@ -105,6 +127,9 @@ struct _GtkConstraintLayout
* parent widget, using the public API objects.
*/
GHashTable *constraints;
+
+ /* HashSet<GtkConstraintGuide> */
+ GHashTable *guides;
};
G_DEFINE_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK_TYPE_LAYOUT_CHILD)
@@ -154,44 +179,20 @@ get_attribute_name (GtkConstraintAttribute attr)
}
static GtkConstraintVariable *
-get_child_attribute (GtkConstraintLayoutChild *self,
- GtkConstraintSolver *solver,
- GtkWidget *widget,
- GtkConstraintAttribute attr)
+get_attribute (ConstraintSolverChildData *self,
+ GtkConstraintSolver *solver,
+ const char *prefix,
+ GtkConstraintAttribute attr)
{
- GtkTextDirection text_dir;
const char *attr_name;
GtkConstraintVariable *res;
- g_assert (attr != GTK_CONSTRAINT_ATTRIBUTE_NONE);
-
- /* Resolve the start/end attributes depending on the layout's text direction */
- if (attr == GTK_CONSTRAINT_ATTRIBUTE_START)
- {
- text_dir = gtk_widget_get_direction (widget);
- if (text_dir == GTK_TEXT_DIR_RTL)
- attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
- else
- attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
- }
- else if (attr == GTK_CONSTRAINT_ATTRIBUTE_END)
- {
- text_dir = gtk_widget_get_direction (widget);
- if (text_dir == GTK_TEXT_DIR_RTL)
- attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
- else
- attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
- }
-
attr_name = get_attribute_name (attr);
res = g_hash_table_lookup (self->bound_attributes, attr_name);
if (res != NULL)
return res;
- res = gtk_constraint_solver_create_variable (solver,
- gtk_widget_get_name (widget),
- attr_name,
- 0.0);
+ res = gtk_constraint_solver_create_variable (solver, prefix, attr_name, 0.0);
g_hash_table_insert (self->bound_attributes, (gpointer) attr_name, res);
/* Some attributes are really constraints computed from other
@@ -207,8 +208,8 @@ get_child_attribute (GtkConstraintLayoutChild *self,
GtkConstraintVariable *left, *width;
GtkConstraintExpression *expr;
- left = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
- width = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+ left = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+ width = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, left);
@@ -229,8 +230,8 @@ get_child_attribute (GtkConstraintLayoutChild *self,
GtkConstraintVariable *top, *height;
GtkConstraintExpression *expr;
- top = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
- height = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+ top = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+ height = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, top);
@@ -251,8 +252,8 @@ get_child_attribute (GtkConstraintLayoutChild *self,
GtkConstraintVariable *left, *width;
GtkConstraintExpression *expr;
- left = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
- width = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+ left = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+ width = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, width);
@@ -275,8 +276,8 @@ get_child_attribute (GtkConstraintLayoutChild *self,
GtkConstraintVariable *top, *height;
GtkConstraintExpression *expr;
- top = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
- height = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+ top = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+ height = get_attribute (self, solver, prefix, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, height);
@@ -325,12 +326,81 @@ get_child_attribute (GtkConstraintLayoutChild *self,
return res;
}
+static GtkConstraintAttribute
+resolve_direction (GtkConstraintAttribute attr,
+ GtkWidget *widget)
+{
+ GtkTextDirection text_dir;
+
+ /* Resolve the start/end attributes depending on the layout's text direction */
+
+ if (widget)
+ text_dir = gtk_widget_get_direction (widget);
+ else
+ text_dir = GTK_TEXT_DIR_LTR;
+
+ if (attr == GTK_CONSTRAINT_ATTRIBUTE_START)
+ {
+ if (text_dir == GTK_TEXT_DIR_RTL)
+ attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
+ else
+ attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+ }
+ else if (attr == GTK_CONSTRAINT_ATTRIBUTE_END)
+ {
+ if (text_dir == GTK_TEXT_DIR_RTL)
+ attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+ else
+ attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
+ }
+
+ return attr;
+}
+
+static GtkConstraintVariable *
+get_child_attribute (GtkConstraintLayoutChild *self,
+ GtkConstraintSolver *solver,
+ GtkWidget *widget,
+ GtkConstraintAttribute attr)
+{
+ const char *prefix = gtk_widget_get_name (widget);
+
+ attr = resolve_direction (attr, widget);
+
+ return get_attribute (&self->data, solver, prefix, attr);
+}
+
+static GtkConstraintVariable *
+get_guide_attribute (GtkConstraintLayout *layout,
+ GtkConstraintGuide *guide,
+ GtkConstraintSolver *solver,
+ GtkConstraintAttribute attr)
+{
+ GtkLayoutManager *manager = GTK_LAYOUT_MANAGER (layout);
+ GtkWidget *widget = gtk_layout_manager_get_widget (manager);
+
+ attr = resolve_direction (attr, widget);
+
+ return get_attribute (&guide->data, solver, "guide", attr);
+}
+
+static void
+clear_constraint_solver_data (GtkConstraintSolver *solver,
+ ConstraintSolverChildData *data)
+{
+ g_clear_pointer (&data->bound_attributes, g_hash_table_unref);
+}
+
static void
gtk_constraint_layout_child_finalize (GObject *gobject)
{
GtkConstraintLayoutChild *self = GTK_CONSTRAINT_LAYOUT_CHILD (gobject);
+ GtkLayoutManager *manager;
+ GtkConstraintSolver *solver;
- g_clear_pointer (&self->bound_attributes, g_hash_table_unref);
+ manager = gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (self));
+ solver = gtk_constraint_layout_get_solver (GTK_CONSTRAINT_LAYOUT (manager));
+ clear_constraint_solver_data (solver, &self->data);
G_OBJECT_CLASS (gtk_constraint_layout_child_parent_class)->finalize (gobject);
}
@@ -346,7 +416,7 @@ gtk_constraint_layout_child_class_init (GtkConstraintLayoutChildClass *klass)
static void
gtk_constraint_layout_child_init (GtkConstraintLayoutChild *self)
{
- self->bound_attributes =
+ self->data.bound_attributes =
g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify) gtk_constraint_variable_unref);
@@ -361,6 +431,7 @@ gtk_constraint_layout_finalize (GObject *gobject)
g_clear_pointer (&self->bound_attributes, g_hash_table_unref);
g_clear_pointer (&self->constraints, g_hash_table_unref);
+ g_clear_pointer (&self->guides, g_hash_table_unref);
G_OBJECT_CLASS (gtk_constraint_layout_parent_class)->finalize (gobject);
}
@@ -553,7 +624,7 @@ layout_add_constraint (GtkConstraintLayout *self,
GtkConstraintExpression *expr;
GtkConstraintSolver *solver;
GtkConstraintAttribute attr;
- GtkWidget *target_widget, *source_widget;
+ GtkConstraintTarget *target, *source;
GtkWidget *layout_widget;
if (gtk_constraint_is_attached (constraint))
@@ -572,25 +643,33 @@ layout_add_constraint (GtkConstraintLayout *self,
return;
attr = gtk_constraint_get_target_attribute (constraint);
- target_widget = gtk_constraint_get_target_widget (constraint);
- if (target_widget == NULL || target_widget == layout_widget)
+ target = gtk_constraint_get_target (constraint);
+ if (target == NULL || target == GTK_CONSTRAINT_TARGET (layout_widget))
{
/* A NULL target widget is assumed to be referring to the layout itself */
target_attr = get_layout_attribute (self, layout_widget, attr);
}
- else if (gtk_widget_get_parent (target_widget) == layout_widget)
+ else if (GTK_IS_WIDGET (target) &&
+ gtk_widget_get_parent (GTK_WIDGET (target)) == layout_widget)
{
GtkLayoutChild *child_info;
- child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), target_widget);
+ child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), GTK_WIDGET (target));
target_attr = get_child_attribute (GTK_CONSTRAINT_LAYOUT_CHILD (child_info),
solver,
- target_widget,
+ GTK_WIDGET (target),
attr);
}
+ else if (GTK_IS_CONSTRAINT_GUIDE (target))
+ {
+ GtkConstraintGuide *guide;
+
+ guide = (GtkConstraintGuide*)g_hash_table_lookup (self->guides, target);
+ target_attr = get_guide_attribute (self, guide, solver, attr);
+ }
else
{
- g_critical ("Unknown target widget '%s'", gtk_widget_get_name (target_widget));
+ g_critical ("Unknown target widget '%p'", target);
target_attr = NULL;
}
@@ -598,7 +677,7 @@ layout_add_constraint (GtkConstraintLayout *self,
return;
attr = gtk_constraint_get_source_attribute (constraint);
- source_widget = gtk_constraint_get_source_widget (constraint);
+ source = gtk_constraint_get_source (constraint);
/* The constraint is a constant */
if (attr == GTK_CONSTRAINT_ATTRIBUTE_NONE)
@@ -607,23 +686,31 @@ layout_add_constraint (GtkConstraintLayout *self,
}
else
{
- if (source_widget == NULL || source_widget == layout_widget)
+ if (source == NULL || source == GTK_CONSTRAINT_TARGET (layout_widget))
{
source_attr = get_layout_attribute (self, layout_widget, attr);
}
- else if (gtk_widget_get_parent (source_widget) == layout_widget)
+ else if (GTK_IS_WIDGET (source) &&
+ gtk_widget_get_parent (GTK_WIDGET (source)) == layout_widget)
{
GtkLayoutChild *child_info;
- child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), source_widget);
+ child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), GTK_WIDGET (source));
source_attr = get_child_attribute (GTK_CONSTRAINT_LAYOUT_CHILD (child_info),
solver,
- source_widget,
+ GTK_WIDGET (source),
attr);
}
+ else if (GTK_IS_CONSTRAINT_GUIDE (source))
+ {
+ GtkConstraintGuide *guide;
+
+ guide = (GtkConstraintGuide*)g_hash_table_lookup (self->guides, source);
+ source_attr = get_guide_attribute (self, guide, solver, attr);
+ }
else
{
- g_critical ("Unknown source widget '%s'", gtk_widget_get_name (source_widget));
+ g_critical ("Unknown source widget '%p'", source);
source_attr = NULL;
return;
}
@@ -871,7 +958,6 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
gtk_widget_get_preferred_size (child, &min_req, &nat_req);
-
width_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
@@ -972,6 +1058,11 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
gtk_constraint_solver_remove_constraint (solver, stay_l);
}
+static void update_min_width (GtkConstraintGuide *guide);
+static void update_nat_width (GtkConstraintGuide *guide);
+static void update_min_height (GtkConstraintGuide *guide);
+static void update_nat_height (GtkConstraintGuide *guide);
+
static void
gtk_constraint_layout_root (GtkLayoutManager *manager)
{
@@ -991,9 +1082,18 @@ gtk_constraint_layout_root (GtkLayoutManager *manager)
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GtkConstraint *constraint = key;
-
layout_add_constraint (self, constraint);
}
+
+ g_hash_table_iter_init (&iter, self->guides);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ GtkConstraintGuide *guide = key;
+ update_min_width (guide);
+ update_nat_width (guide);
+ update_min_height (guide);
+ update_nat_height (guide);
+ }
}
static void
@@ -1011,7 +1111,6 @@ gtk_constraint_layout_unroot (GtkLayoutManager *manager)
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GtkConstraint *constraint = key;
-
gtk_constraint_detach (constraint);
}
@@ -1047,6 +1146,11 @@ gtk_constraint_layout_init (GtkConstraintLayout *self)
g_hash_table_new_full (NULL, NULL,
(GDestroyNotify) g_object_unref,
NULL);
+
+ self->guides =
+ g_hash_table_new_full (NULL, NULL,
+ (GDestroyNotify) g_object_unref,
+ NULL);
}
/**
@@ -1116,3 +1220,368 @@ gtk_constraint_layout_remove_constraint (GtkConstraintLayout *manager,
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (manager));
}
+
+static void
+gtk_constraint_guide_constraint_target_iface_init (GtkConstraintTargetInterface *iface)
+{
+}
+
+struct _GtkConstraintGuideClass {
+ GObjectClass parent_class;
+};
+
+enum {
+ PROP_MIN_WIDTH = 1,
+ PROP_MIN_HEIGHT,
+ PROP_NAT_WIDTH,
+ PROP_NAT_HEIGHT,
+ LAST_PROP
+};
+
+static GParamSpec *guide_props[LAST_PROP];
+
+G_DEFINE_TYPE_WITH_CODE (GtkConstraintGuide, gtk_constraint_guide, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_CONSTRAINT_TARGET,
+ gtk_constraint_guide_constraint_target_iface_init))
+
+static void
+gtk_constraint_guide_init (GtkConstraintGuide *guide)
+{
+ guide->data.bound_attributes =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ (GDestroyNotify) gtk_constraint_variable_unref);
+}
+
+static void
+update_min_width (GtkConstraintGuide *guide)
+{
+ GtkConstraintSolver *solver;
+ GtkConstraintVariable *var;
+
+ if (!guide->layout)
+ return;
+
+ solver = guide->layout->solver;
+
+ if (!solver)
+ return;
+
+ if (guide->width_constraint[0] != NULL)
+ gtk_constraint_solver_remove_constraint (solver, guide->width_constraint[0]);
+
+ var = get_guide_attribute (guide->layout, guide, solver, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+ guide->width_constraint[0] =
+ gtk_constraint_solver_add_constraint (solver,
+ var,
+ GTK_CONSTRAINT_RELATION_GE,
+ gtk_constraint_expression_new (guide->min_width),
+ GTK_CONSTRAINT_WEIGHT_REQUIRED);
+}
+
+static void
+update_min_height (GtkConstraintGuide *guide)
+{
+ GtkConstraintSolver *solver;
+ GtkConstraintVariable *var;
+
+ if (!guide->layout)
+ return;
+
+ solver = guide->layout->solver;
+
+ if (!solver)
+ return;
+
+ if (guide->height_constraint[0] != NULL)
+ gtk_constraint_solver_remove_constraint (solver, guide->height_constraint[0]);
+
+ var = get_guide_attribute (guide->layout, guide, solver, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+ guide->height_constraint[0] =
+ gtk_constraint_solver_add_constraint (solver,
+ var,
+ GTK_CONSTRAINT_RELATION_GE,
+ gtk_constraint_expression_new (guide->min_height),
+ GTK_CONSTRAINT_WEIGHT_REQUIRED);
+}
+
+static void
+update_nat_width (GtkConstraintGuide *guide)
+{
+ GtkConstraintSolver *solver;
+ GtkConstraintVariable *var;
+
+ if (!guide->layout)
+ return;
+
+ solver = guide->layout->solver;
+
+ if (!solver)
+ return;
+
+ if (guide->width_constraint[1] != NULL)
+ gtk_constraint_solver_remove_constraint (solver, guide->width_constraint[1]);
+
+ var = get_guide_attribute (guide->layout, guide, solver, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+ guide->width_constraint[1] =
+ gtk_constraint_solver_add_constraint (solver,
+ var,
+ GTK_CONSTRAINT_RELATION_EQ,
+ gtk_constraint_expression_new (guide->nat_width),
+ GTK_CONSTRAINT_WEIGHT_MEDIUM);
+}
+
+static void
+update_nat_height (GtkConstraintGuide *guide)
+{
+ GtkConstraintSolver *solver;
+ GtkConstraintVariable *var;
+
+ if (!guide->layout)
+ return;
+
+ solver = guide->layout->solver;
+
+ if (!solver)
+ return;
+
+ if (guide->height_constraint[1] != NULL)
+ gtk_constraint_solver_remove_constraint (solver, guide->height_constraint[1]);
+
+ var = get_guide_attribute (guide->layout, guide, solver, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+ guide->height_constraint[1] =
+ gtk_constraint_solver_add_constraint (solver,
+ var,
+ GTK_CONSTRAINT_RELATION_EQ,
+ gtk_constraint_expression_new (guide->nat_height),
+ GTK_CONSTRAINT_WEIGHT_MEDIUM);
+}
+
+static void
+set_min_width (GtkConstraintGuide *guide,
+ int min_width)
+{
+ if (guide->min_width == min_width)
+ return;
+
+ guide->min_width = min_width;
+ g_object_notify_by_pspec (G_OBJECT (guide),
+ guide_props[PROP_MIN_WIDTH]);
+
+ update_min_width (guide);
+}
+
+static void
+set_min_height (GtkConstraintGuide *guide,
+ int min_height)
+{
+ if (guide->min_height == min_height)
+ return;
+
+ guide->min_height = min_height;
+ g_object_notify_by_pspec (G_OBJECT (guide),
+ guide_props[PROP_MIN_HEIGHT]);
+
+ update_min_height (guide);
+}
+
+static void
+set_nat_width (GtkConstraintGuide *guide,
+ int nat_width)
+{
+ if (guide->nat_width == nat_width)
+ return;
+
+ guide->nat_width = nat_width;
+ g_object_notify_by_pspec (G_OBJECT (guide),
+ guide_props[PROP_NAT_WIDTH]);
+
+ update_nat_width (guide);
+}
+static void
+set_nat_height (GtkConstraintGuide *guide,
+ int nat_height)
+{
+ if (guide->nat_height == nat_height)
+ return;
+
+ guide->nat_height = nat_height;
+ g_object_notify_by_pspec (G_OBJECT (guide),
+ guide_props[PROP_NAT_HEIGHT]);
+
+ update_nat_height (guide);
+}
+
+static void
+gtk_constraint_guide_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkConstraintGuide *self = GTK_CONSTRAINT_GUIDE (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_MIN_WIDTH:
+ set_min_width (self, g_value_get_int (value));
+ break;
+
+ case PROP_MIN_HEIGHT:
+ set_min_height (self, g_value_get_int (value));
+ break;
+
+ case PROP_NAT_WIDTH:
+ set_nat_width (self, g_value_get_int (value));
+ break;
+
+ case PROP_NAT_HEIGHT:
+ set_nat_height (self, g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_constraint_guide_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkConstraintGuide *self = GTK_CONSTRAINT_GUIDE (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_MIN_WIDTH:
+ g_value_set_int (value, self->min_width);
+ break;
+
+ case PROP_MIN_HEIGHT:
+ g_value_set_int (value, self->min_height);
+ break;
+
+ case PROP_NAT_WIDTH:
+ g_value_set_int (value, self->nat_width);
+ break;
+
+ case PROP_NAT_HEIGHT:
+ g_value_set_int (value, self->nat_height);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_constraint_guide_finalize (GObject *object)
+{
+ GtkConstraintGuide *self = GTK_CONSTRAINT_GUIDE (object);
+ GtkConstraintSolver *solver;
+
+ if (self->layout)
+ {
+ solver = gtk_constraint_layout_get_solver (self->layout);
+ clear_constraint_solver_data (solver, &self->data);
+ }
+
+ G_OBJECT_CLASS (gtk_constraint_guide_parent_class)->finalize (object);
+}
+
+static void
+gtk_constraint_guide_class_init (GtkConstraintGuideClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = gtk_constraint_guide_finalize;
+ object_class->set_property = gtk_constraint_guide_set_property;
+ object_class->get_property = gtk_constraint_guide_get_property;
+
+ guide_props[PROP_MIN_WIDTH] =
+ g_param_spec_int ("min-width",
+ "Minimum width",
+ "Minimum width",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE|
+ G_PARAM_EXPLICIT_NOTIFY);
+ guide_props[PROP_MIN_HEIGHT] =
+ g_param_spec_int ("min-height",
+ "Minimum height",
+ "Minimum height",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE|
+ G_PARAM_EXPLICIT_NOTIFY);
+ guide_props[PROP_NAT_WIDTH] =
+ g_param_spec_int ("nat-width",
+ "Natural width",
+ "Natural width",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE|
+ G_PARAM_EXPLICIT_NOTIFY);
+ guide_props[PROP_NAT_HEIGHT] =
+ g_param_spec_int ("nat-height",
+ "Natural height",
+ "Natural height",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE|
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, LAST_PROP, guide_props);
+}
+
+/**
+ * gtk_constraint_layout_add_guide:
+ * @layout: a #GtkConstraintLayout
+ * @guide: (transfer full): a #GtkConstraintGuide object
+ *
+ * Adds a guide to @layout. A guide can be used as
+ * the source or target of constraints, like a widget,
+ * but it is not visible.
+ *
+ * The @manager acquires the ownership of @guide after calling
+ * this function.
+ */
+void
+gtk_constraint_layout_add_guide (GtkConstraintLayout *layout,
+ GtkConstraintGuide *guide)
+{
+ g_return_if_fail (GTK_IS_CONSTRAINT_LAYOUT (layout));
+ g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
+ g_return_if_fail (guide->layout == NULL);
+
+ guide->layout = layout;
+
+ g_hash_table_add (layout->guides, guide);
+
+ gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout));
+}
+
+/**
+ * gtk_constraint_layout_remove_guide:
+ * @layout: a #GtkConstraintManager
+ * @guide: a #GtkConstraintGuide object
+ *
+ * Removes @guide from the layout manager,
+ * so that it no longer influences the layout.
+ */
+void
+gtk_constraint_layout_remove_guide (GtkConstraintLayout *layout,
+ GtkConstraintGuide *guide)
+{
+ GtkConstraintSolver *solver;
+
+ g_return_if_fail (GTK_IS_CONSTRAINT_LAYOUT (layout));
+ g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
+ g_return_if_fail (guide->layout == layout);
+
+ solver = gtk_constraint_layout_get_solver (guide->layout);
+ clear_constraint_solver_data (solver, &guide->data);
+ guide->layout = NULL;
+
+ g_hash_table_remove (layout->guides, guide);
+
+ gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout));
+}
diff --git a/gtk/gtkconstraintlayout.h b/gtk/gtkconstraintlayout.h
index b5eba7b125..758fcefb20 100644
--- a/gtk/gtkconstraintlayout.h
+++ b/gtk/gtkconstraintlayout.h
@@ -25,6 +25,30 @@ G_BEGIN_DECLS
#define GTK_TYPE_CONSTRAINT_LAYOUT (gtk_constraint_layout_get_type ())
#define GTK_TYPE_CONSTRAINT_LAYOUT_CHILD (gtk_constraint_layout_child_get_type ())
+#define GTK_TYPE_CONSTRAINT_GUIDE (gtk_constraint_guide_get_type ())
+
+/**
+ * GtkConstraintLayoutChild:
+ *
+ * A #GtkLayoutChild in a #GtkConstraintLayout.
+ */
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK, CONSTRAINT_LAYOUT_CHILD, GtkLayoutChild)
+
+/**
+ * GtkConstraintGuide:
+ *
+ * An object that can be added to a #GtkConstraintLayout and be
+ * used in constraints like a widget, without being drawn. Guides
+ * have a minimal and natural size. Depending on the constraints
+ * that are applied, they can act like a guideline that widgets
+ * can be aligned to, or like 'flexible space'.
+ */
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkConstraintGuide, gtk_constraint_guide, GTK, CONSTRAINT_GUIDE, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkConstraintGuide * gtk_constraint_guide_new (void);
/**
* GtkConstraintLayout:
@@ -45,12 +69,11 @@ GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_remove_constraint (GtkConstraintLayout *manager,
GtkConstraint *constraint);
-/**
- * GtkConstraintLayoutChild:
- *
- * A #GtkLayoutChild in a #GtkConstraintLayout.
- */
GDK_AVAILABLE_IN_ALL
-G_DECLARE_FINAL_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK, CONSTRAINT_LAYOUT_CHILD, GtkLayoutChild)
+void gtk_constraint_layout_add_guide (GtkConstraintLayout *manager,
+ GtkConstraintGuide *guide);
+GDK_AVAILABLE_IN_ALL
+void gtk_constraint_layout_remove_guide (GtkConstraintLayout *manager,
+ GtkConstraintGuide *guide);
G_END_DECLS