[gtk/wip/ebassi/constraint-layout: 7/9] Add GtkConstraintGuide



commit ffd8b19a87a269033fab5fa217c9597a008e2aba
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jun 25 19:38:32 2019 -0400

    Add GtkConstraintGuide
    
    This is meant to be a flexible space.

 demos/gtk-demo/constraints.c |  21 +-
 gtk/gtkconstraint.c          |   2 +-
 gtk/gtkconstraintlayout.c    | 585 ++++++++++++++++++++++++++++++++++++++-----
 gtk/gtkconstraintlayout.h    |  35 ++-
 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,
@@ -94,12 +104,21 @@ build_constraints (SimpleGrid          *self,
                         GTK_CONSTRAINT_STRENGTH_REQUIRED));
   gtk_constraint_layout_add_constraint (manager,
     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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]