Compare commits

...

11 Commits

Author SHA1 Message Date
Matthias Clasen
463dffe48d constraint solver: Fix repeat suggestions
We were not storing the previous value, causing
the first two suggestions to work, but not later
ones.

Fixes the test added in the previous commit.
2019-06-28 16:59:39 +00:00
Matthias Clasen
d42c26b117 Amend a constraint solver test
Make the 'repeat edit' test make more than to
suggestions in a single edit phase. It turns out
that this does not work, whereas just doing
two in a row does.
2019-06-28 16:36:46 +00:00
Matthias Clasen
12d45c01b0 Add an interactive constraints demo 2019-06-28 13:11:22 +01:00
Matthias Clasen
fc3f4aafe5 Make the constraints demo more interesting
Add a max size to the buttons, to force the
space to open up.
2019-06-28 13:11:22 +01:00
Matthias Clasen
ffd8b19a87 Add GtkConstraintGuide
This is meant to be a flexible space.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
20c4a4bb21 Use generic pointers for constraint targets
Since GtkWidget implements GtkConstraintTarget, we can omit the explicit
cast, and validate the type at run time.
2019-06-28 13:11:22 +01:00
Matthias Clasen
f8647b02eb Redefine constraints with GtkConstraintTarget
This is in preparation for allowing non-widgets
to act as constraint targets.
2019-06-28 13:11:22 +01:00
Matthias Clasen
4b11e73be6 widget: Implement GtkConstraintTarget 2019-06-28 13:11:22 +01:00
Matthias Clasen
4de3e99fbe Add GtkConstraintTarget
This is an marker interface that we will
use to accept other things that widgets
in constraints.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
c81e04755d Notify a layout change when adding and removing constraints
Changing the set of constraints should cause a relayout.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
e4466dd4fe Fix the opposite size measurement in GtkConstraintLayout
We cannot use the given "for size" when querying our children, because
the constraint layout has no idea about the opposite size of its
children until the layout is complete.

Additionally, we should only suggest an opposite size for the layout if
we have one, instead of suggesting a weak zero size.
2019-06-28 13:11:22 +01:00
12 changed files with 1017 additions and 182 deletions

View File

@@ -1,4 +1,4 @@
/* Constraints
/* Constraints/Simple
*
* GtkConstraintLayout provides a layout manager that uses relations
* between widgets (also known as "constraints") to compute the position
@@ -74,6 +74,22 @@ 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_constant (GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_LE,
200.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
@@ -94,12 +110,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,

View File

@@ -0,0 +1,245 @@
/* Constraints/Interactive
*
* Demonstrate how constraints can be updates during
* user interaction.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE (InteractiveGrid, interactive_grid, INTERACTIVE, GRID, GtkWidget)
struct _InteractiveGrid
{
GtkWidget parent_instance;
GtkWidget *button1, *button2;
GtkWidget *button3;
GtkConstraintGuide *guide;
GtkConstraint *constraint;
};
G_DEFINE_TYPE (InteractiveGrid, interactive_grid, GTK_TYPE_WIDGET)
static void
interactive_grid_destroy (GtkWidget *widget)
{
InteractiveGrid *self = INTERACTIVE_GRID (widget);
g_clear_pointer (&self->button1, gtk_widget_destroy);
g_clear_pointer (&self->button2, gtk_widget_destroy);
g_clear_pointer (&self->button3, gtk_widget_destroy);
GTK_WIDGET_CLASS (interactive_grid_parent_class)->destroy (widget);
}
static void
interactive_grid_class_init (InteractiveGridClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->destroy = interactive_grid_destroy;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
static void
build_constraints (InteractiveGrid *self,
GtkConstraintLayout *manager)
{
self->guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE, NULL);
gtk_constraint_layout_add_guide (manager, self->guide);
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new_constant (GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_EQ,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
}
static void
drag_cb (GtkGestureDrag *drag,
double offset_x,
double offset_y,
InteractiveGrid *self)
{
GtkConstraintLayout *layout = GTK_CONSTRAINT_LAYOUT (gtk_widget_get_layout_manager (GTK_WIDGET (self)));
double x, y;
if (self->constraint)
{
gtk_constraint_layout_remove_constraint (layout, self->constraint);
g_clear_object (&self->constraint);
}
gtk_gesture_drag_get_start_point (drag, &x, &y);
self->constraint = gtk_constraint_new_constant (GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
GTK_CONSTRAINT_RELATION_EQ,
x + offset_x,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (layout, g_object_ref (self->constraint));
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
static void
interactive_grid_init (InteractiveGrid *self)
{
GtkWidget *widget = GTK_WIDGET (self);
GtkGesture *drag;
self->button1 = gtk_button_new_with_label ("Child 1");
gtk_widget_set_parent (self->button1, widget);
gtk_widget_set_name (self->button1, "button1");
self->button2 = gtk_button_new_with_label ("Child 2");
gtk_widget_set_parent (self->button2, widget);
gtk_widget_set_name (self->button2, "button2");
self->button3 = gtk_button_new_with_label ("Child 3");
gtk_widget_set_parent (self->button3, widget);
gtk_widget_set_name (self->button3, "button3");
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
build_constraints (self, GTK_CONSTRAINT_LAYOUT (manager));
drag = gtk_gesture_drag_new ();
g_signal_connect (drag, "drag-update", G_CALLBACK (drag_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag));
}
GtkWidget *
do_constraints2 (GtkWidget *do_widget)
{
static GtkWidget *window;
if (!window)
{
GtkWidget *header, *box, *grid, *button;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
header = gtk_header_bar_new ();
gtk_header_bar_set_title (GTK_HEADER_BAR (header), "Constraints");
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), FALSE);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_add (GTK_CONTAINER (window), box);
grid = g_object_new (interactive_grid_get_type (), NULL);
gtk_widget_set_hexpand (grid, TRUE);
gtk_widget_set_vexpand (grid, TRUE);
gtk_container_add (GTK_CONTAINER (box), grid);
button = gtk_button_new_with_label ("Close");
gtk_container_add (GTK_CONTAINER (box), button);
gtk_widget_set_hexpand (grid, TRUE);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (gtk_widget_destroy), window);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -151,6 +151,7 @@
<file>colorsel.c</file>
<file>combobox.c</file>
<file>constraints.c</file>
<file>constraints2.c</file>
<file>css_accordion.c</file>
<file>css_basics.c</file>
<file>css_blendmodes.c</file>

View File

@@ -9,6 +9,7 @@ demos = files([
'colorsel.c',
'combobox.c',
'constraints.c',
'constraints2.c',
'css_accordion.c',
'css_basics.c',
'css_blendmodes.c',

View File

@@ -46,10 +46,10 @@
#include "gtkwidget.h"
enum {
PROP_TARGET_WIDGET = 1,
PROP_TARGET = 1,
PROP_TARGET_ATTRIBUTE,
PROP_RELATION,
PROP_SOURCE_WIDGET,
PROP_SOURCE,
PROP_SOURCE_ATTRIBUTE,
PROP_MULTIPLIER,
PROP_CONSTANT,
@@ -72,8 +72,8 @@ gtk_constraint_set_property (GObject *gobject,
switch (prop_id)
{
case PROP_TARGET_WIDGET:
self->target_widget = g_value_get_object (value);
case PROP_TARGET:
self->target = g_value_get_object (value);
break;
case PROP_TARGET_ATTRIBUTE:
@@ -84,8 +84,8 @@ gtk_constraint_set_property (GObject *gobject,
self->relation = g_value_get_enum (value);
break;
case PROP_SOURCE_WIDGET:
self->source_widget = g_value_get_object (value);
case PROP_SOURCE:
self->source = g_value_get_object (value);
break;
case PROP_SOURCE_ATTRIBUTE:
@@ -120,8 +120,8 @@ gtk_constraint_get_property (GObject *gobject,
switch (prop_id)
{
case PROP_TARGET_WIDGET:
g_value_set_object (value, self->target_widget);
case PROP_TARGET:
g_value_set_object (value, self->target);
break;
case PROP_TARGET_ATTRIBUTE:
@@ -132,8 +132,8 @@ gtk_constraint_get_property (GObject *gobject,
g_value_set_enum (value, self->relation);
break;
case PROP_SOURCE_WIDGET:
g_value_set_object (value, self->source_widget);
case PROP_SOURCE:
g_value_set_object (value, self->source);
break;
case PROP_SOURCE_ATTRIBUTE:
@@ -178,31 +178,31 @@ gtk_constraint_class_init (GtkConstraintClass *klass)
gobject_class->finalize = gtk_constraint_finalize;
/**
* GtkConstraint:target-widget:
* GtkConstraint:target:
*
* The target widget of the constraint.
* The target of the constraint.
*
* The constraint will set the #GtkConstraint:target-attribute of the
* target widget using the #GtkConstraint:source-attribute of the source
* target using the #GtkConstraint:source-attribute of the source
* widget.
*/
obj_props[PROP_TARGET_WIDGET] =
g_param_spec_object ("target-widget",
P_("Target Widget"),
P_("The target widget of the constraint"),
GTK_TYPE_WIDGET,
obj_props[PROP_TARGET] =
g_param_spec_object ("target",
P_("Target"),
P_("The target of the constraint"),
GTK_TYPE_CONSTRAINT_TARGET,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:target-attribute:
*
* The attribute of the #GtkConstraint:target-widget set by the constraint.
* The attribute of the #GtkConstraint:target set by the constraint.
*/
obj_props[PROP_TARGET_ATTRIBUTE] =
g_param_spec_enum ("target-attribute",
P_("Target Attribute"),
P_("The attribute of the target widget set by the constraint"),
P_("The attribute of the target set by the constraint"),
GTK_TYPE_CONSTRAINT_ATTRIBUTE,
GTK_CONSTRAINT_ATTRIBUTE_NONE,
G_PARAM_READWRITE |
@@ -223,26 +223,25 @@ gtk_constraint_class_init (GtkConstraintClass *klass)
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:source-widget:
* GtkConstraint:source:
*
* The source widget of the constraint.
* The source of the constraint.
*
* The constraint will set the #GtkConstraint:target-attribute of the
* target widget using the #GtkConstraint:source-attribute of the source
* widget.
* target using the #GtkConstraint:source-attribute of the source.
*/
obj_props[PROP_SOURCE_WIDGET] =
g_param_spec_object ("source-widget",
P_("Source Widget"),
P_("The source widget of the constraint"),
GTK_TYPE_WIDGET,
obj_props[PROP_SOURCE] =
g_param_spec_object ("source",
P_("Source"),
P_("The source of the constraint"),
GTK_TYPE_CONSTRAINT_TARGET,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:source-attribute:
*
* The attribute of the #GtkConstraint:source-widget read by the constraint.
* The attribute of the #GtkConstraint:source read by the constraint.
*/
obj_props[PROP_SOURCE_ATTRIBUTE] =
g_param_spec_enum ("source-attribute",
@@ -316,39 +315,38 @@ gtk_constraint_init (GtkConstraint *self)
/**
* gtk_constraint_new:
* @target_widget: (nullable): a #GtkWidget
* @target_attribute: the attribute of @target_widget to be set
* @target: (nullable) (type GtkConstraintTarget): a #GtkConstraintTarget
* @target_attribute: the attribute of @target to be set
* @relation: the relation equivalence between @target_attribute and @source_attribute
* @source_widget: (nullable): a #GtkWidget
* @source_attribute: the attribute of @source_widget to be read
* @source: (nullable) (type GtkConstraintTarget): a #GtkConstraintTarget
* @source_attribute: the attribute of @source to be read
* @multiplier: a multiplication factor to be applied to @source_attribute
* @constant: a constant factor to be added to @source_attribute
* @strength: the strength of the constraint
*
* Creates a new #GtkConstraint representing a relation between a layout
* attribute on a source #GtkWidget and a layout attribute on a target
* #GtkWidget.
* attribute on a source and a layout attribute on a target.
*
* Returns: the newly created #GtkConstraint
*/
GtkConstraint *
gtk_constraint_new (GtkWidget *target_widget,
gtk_constraint_new (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
GtkWidget *source_widget,
gpointer source,
GtkConstraintAttribute source_attribute,
double multiplier,
double constant,
int strength)
{
g_return_val_if_fail (target_widget == NULL || GTK_IS_WIDGET (target_widget), NULL);
g_return_val_if_fail (source_widget == NULL || GTK_IS_WIDGET (source_widget), NULL);
g_return_val_if_fail (target == NULL || GTK_IS_CONSTRAINT_TARGET (target), NULL);
g_return_val_if_fail (source == NULL || GTK_IS_CONSTRAINT_TARGET (source), NULL);
return g_object_new (GTK_TYPE_CONSTRAINT,
"target-widget", target_widget,
"target", target,
"target-attribute", target_attribute,
"relation", relation,
"source-widget", source_widget,
"source", source,
"source-attribute", source_attribute,
"multiplier", multiplier,
"constant", constant,
@@ -358,28 +356,28 @@ gtk_constraint_new (GtkWidget *target_widget,
/**
* gtk_constraint_new_constant:
* @target_widget: (nullable): a #GtkWidget
* @target_attribute: the attribute of @target_widget to be set
* @target: (nullable) (type GtkConstraintTarget): a #GtkConstraintTarget
* @target_attribute: the attribute of @target to be set
* @relation: the relation equivalence between @target_attribute and @constant
* @constant: a constant factor to be set on @target_attribute
* @strength: the strength of the constraint
*
* Creates a new #GtkConstraint representing a relation between a layout
* attribute on a target #GtkWidget and a constant value.
* attribute on a target and a constant value.
*
* Returns: the newly created #GtkConstraint
*/
GtkConstraint *
gtk_constraint_new_constant (GtkWidget *target_widget,
gtk_constraint_new_constant (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
double constant,
int strength)
{
g_return_val_if_fail (target_widget == NULL || GTK_IS_WIDGET (target_widget), NULL);
g_return_val_if_fail (target == NULL || GTK_IS_CONSTRAINT_TARGET (target), NULL);
return g_object_new (GTK_TYPE_CONSTRAINT,
"target-widget", target_widget,
"target", target,
"target-attribute", target_attribute,
"relation", relation,
"source-attribute", GTK_CONSTRAINT_ATTRIBUTE_NONE,
@@ -401,7 +399,18 @@ gtk_constraint_get_target_widget (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
return constraint->target_widget;
if (GTK_IS_WIDGET (constraint->target))
return GTK_WIDGET (constraint->target);
return NULL;
}
GtkConstraintTarget *
gtk_constraint_get_target (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
return constraint->target;
}
GtkConstraintAttribute
@@ -425,7 +434,18 @@ gtk_constraint_get_source_widget (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
return constraint->source_widget;
if (GTK_IS_WIDGET (constraint->source))
return GTK_WIDGET (constraint->source);
return NULL;
}
GtkConstraintTarget *
gtk_constraint_get_source (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
return constraint->source;
}
GtkConstraintAttribute
@@ -552,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;
}
@@ -581,3 +601,17 @@ gtk_constraint_detach (GtkConstraint *constraint)
constraint->constraint_ref = NULL;
constraint->solver = NULL;
}
typedef struct _GtkConstraintTargetInterface GtkConstraintTargetInterface;
struct _GtkConstraintTargetInterface
{
GTypeInterface g_iface;
};
G_DEFINE_INTERFACE (GtkConstraintTarget, gtk_constraint_target, G_TYPE_OBJECT)
static void
gtk_constraint_target_default_init (GtkConstraintTargetInterface *iface)
{
}

View File

@@ -24,6 +24,21 @@
G_BEGIN_DECLS
typedef struct _GtkConstraintTarget GtkConstraintTarget;
#define GTK_TYPE_CONSTRAINT_TARGET (gtk_constraint_target_get_type ())
/**
* GtkConstraintTarget:
*
* The GtkConstraintTarget interface is implemented by objects that
* can be used as source or target in #GtkConstraints. Besides
* #GtkWidget, it is also implemented by #GtkConstraintGuide.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GtkConstraintTarget, gtk_constraint_target, GTK, CONSTRAINT_TARGET, GObject)
#define GTK_TYPE_CONSTRAINT (gtk_constraint_get_type ())
/**
@@ -43,16 +58,16 @@ GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraint, gtk_constraint, GTK, CONSTRAINT, GObject)
GDK_AVAILABLE_IN_ALL
GtkConstraint * gtk_constraint_new (GtkWidget *target_widget,
GtkConstraint * gtk_constraint_new (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
GtkWidget *source_widget,
gpointer source,
GtkConstraintAttribute source_attribute,
double multiplier,
double constant,
int strength);
GDK_AVAILABLE_IN_ALL
GtkConstraint * gtk_constraint_new_constant (GtkWidget *target_widget,
GtkConstraint * gtk_constraint_new_constant (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
double constant,
@@ -61,10 +76,14 @@ GtkConstraint * gtk_constraint_new_constant (GtkWidget
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_constraint_get_target_widget (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintTarget * gtk_constraint_get_target (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintAttribute gtk_constraint_get_target_attribute (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_constraint_get_source_widget (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintTarget * gtk_constraint_get_source (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintAttribute gtk_constraint_get_source_attribute (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintRelation gtk_constraint_get_relation (GtkConstraint *constraint);

View File

@@ -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;
}
@@ -693,57 +780,47 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
gtk_widget_measure (child, orientation, for_size,
gtk_widget_measure (child, orientation, -1,
&min_size, &nat_size,
NULL, NULL);
switch (orientation)
{
case GTK_ORIENTATION_HORIZONTAL:
width_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
width_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
constraint =
gtk_constraint_solver_add_constraint (solver,
width_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (min_size),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (size_constraints, constraint);
constraint =
gtk_constraint_solver_add_constraint (solver,
width_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (min_size),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (size_constraints, constraint);
constraint =
gtk_constraint_solver_add_constraint (solver,
width_var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new (nat_size),
GTK_CONSTRAINT_WEIGHT_MEDIUM);
g_ptr_array_add (size_constraints, constraint);
break;
constraint =
gtk_constraint_solver_add_constraint (solver,
width_var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new (nat_size),
GTK_CONSTRAINT_WEIGHT_MEDIUM);
g_ptr_array_add (size_constraints, constraint);
case GTK_ORIENTATION_VERTICAL:
height_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
height_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
constraint =
gtk_constraint_solver_add_constraint (solver,
height_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (min_size),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (size_constraints, constraint);
constraint =
gtk_constraint_solver_add_constraint (solver,
height_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (min_size),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (size_constraints, constraint);
constraint =
gtk_constraint_solver_add_constraint (solver,
height_var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new (nat_size),
GTK_CONSTRAINT_WEIGHT_MEDIUM);
g_ptr_array_add (size_constraints, constraint);
break;
default:
break;
}
constraint =
gtk_constraint_solver_add_constraint (solver,
height_var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new (nat_size),
GTK_CONSTRAINT_WEIGHT_MEDIUM);
g_ptr_array_add (size_constraints, constraint);
}
switch (orientation)
@@ -769,15 +846,22 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
* natural state of the system. Once we get the value out, we can
* remove these constraints
*/
gtk_constraint_solver_add_edit_variable (solver, size, GTK_CONSTRAINT_WEIGHT_WEAK + 1);
gtk_constraint_solver_add_edit_variable (solver, opposite_size, GTK_CONSTRAINT_WEIGHT_WEAK + 2);
if (for_size > 0)
{
gtk_constraint_solver_add_edit_variable (solver, opposite_size, GTK_CONSTRAINT_WEIGHT_MEDIUM * 2.0);
gtk_constraint_solver_begin_edit (solver);
gtk_constraint_solver_suggest_value (solver, opposite_size, for_size);
gtk_constraint_solver_resolve (solver);
gtk_constraint_solver_begin_edit (solver);
value = gtk_constraint_variable_get_value (size);
gtk_constraint_solver_suggest_value (solver, size, 0.0);
gtk_constraint_solver_suggest_value (solver, opposite_size, for_size >= 0 ? for_size : 0.0);
gtk_constraint_solver_resolve (solver);
gtk_constraint_solver_remove_edit_variable (solver, opposite_size);
gtk_constraint_solver_end_edit (solver);
}
else
{
value = gtk_constraint_variable_get_value (size);
}
GTK_NOTE (LAYOUT,
g_print ("layout %p preferred %s size: %.3f (for opposite size: %d)\n",
@@ -786,13 +870,6 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
gtk_constraint_variable_get_value (size),
for_size));
value = gtk_constraint_variable_get_value (size);
gtk_constraint_solver_remove_edit_variable (solver, size);
gtk_constraint_solver_remove_edit_variable (solver, opposite_size);
gtk_constraint_solver_end_edit (solver);
for (guint i = 0; i < size_constraints->len; i++)
{
GtkConstraintRef *ref = g_ptr_array_index (size_constraints, i);
@@ -881,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);
@@ -982,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)
{
@@ -1001,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
@@ -1021,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);
}
@@ -1057,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);
}
/**
@@ -1079,7 +1173,7 @@ gtk_constraint_layout_new (void)
*
* Adds a #GtkConstraint to the layout manager.
*
* The #GtkConstraint:source-widget and #GtkConstraint:target-widget
* The #GtkConstraint:source and #GtkConstraint:target
* properties of @constraint can be:
*
* - set to %NULL to indicate that the constraint refers to the
@@ -1101,6 +1195,8 @@ gtk_constraint_layout_add_constraint (GtkConstraintLayout *manager,
layout_add_constraint (manager, constraint);
g_hash_table_add (manager->constraints, constraint);
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (manager));
}
/**
@@ -1121,4 +1217,371 @@ gtk_constraint_layout_remove_constraint (GtkConstraintLayout *manager,
gtk_constraint_detach (constraint);
g_hash_table_remove (manager->constraints, constraint);
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));
}

View File

@@ -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

View File

@@ -31,8 +31,8 @@ struct _GtkConstraint
GtkConstraintAttribute target_attribute;
GtkConstraintAttribute source_attribute;
GtkWidget *target_widget;
GtkWidget *source_widget;
GtkConstraintTarget *target;
GtkConstraintTarget *source;
GtkConstraintRelation relation;

View File

@@ -1972,6 +1972,7 @@ gtk_constraint_solver_suggest_value (GtkConstraintSolver *self,
double value)
{
EditInfo *ei = g_hash_table_lookup (self->edit_var_map, variable);
double delta;
if (ei == NULL)
{
g_critical ("Suggesting value '%g' but variable %p is not editable",
@@ -1987,9 +1988,10 @@ gtk_constraint_solver_suggest_value (GtkConstraintSolver *self,
return;
}
ei->prev_constant = value - ei->prev_constant;
delta = value - ei->prev_constant;
ei->prev_constant = value;
gtk_constraint_solver_delta_edit_constant (self, ei->prev_constant, ei->eplus, ei->eminus);
gtk_constraint_solver_delta_edit_constant (self, delta, ei->eplus, ei->eminus);
}
/*< private >

View File

@@ -73,6 +73,7 @@
#include "gtkwindowgroup.h"
#include "gtkwindowprivate.h"
#include "gtknativeprivate.h"
#include "gtkconstraint.h"
#include "a11y/gtkwidgetaccessible.h"
#include "inspector/window.h"
@@ -770,6 +771,13 @@ gtk_widget_get_type (void)
NULL /* interface data */
};
const GInterfaceInfo constraint_target_info =
{
(GInterfaceInitFunc) NULL,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
widget_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED, g_intern_static_string ("GtkWidget"),
&widget_info, G_TYPE_FLAG_ABSTRACT);
@@ -782,6 +790,8 @@ gtk_widget_get_type (void)
&accessibility_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
&buildable_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_CONSTRAINT_TARGET,
&constraint_target_info) ;
}
return widget_type;

View File

@@ -254,15 +254,27 @@ constraint_solver_edit_var_suggest (void)
gtk_constraint_solver_suggest_value (solver, a, 2.0);
gtk_constraint_solver_resolve (solver);
g_test_message ("Check values after first edit");
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_test_message ("Check values after second edit");
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_solver_suggest_value (solver, a, 12.0);
gtk_constraint_solver_resolve (solver);
g_test_message ("Check values after third edit");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 12.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 12.0, 0.001);
gtk_constraint_variable_unref (a);
gtk_constraint_variable_unref (b);