Compare commits

...

21 Commits

Author SHA1 Message Date
Matthias Clasen 615f1aed65 Add an interactive constraint solver test
This is an example described in the original
Cassowary paper, reimplemented from scratch.
It exposes some instability and crashiness
in our solver.
2019-06-28 04:51:18 +00:00
Matthias Clasen 19fdb764a6 constraint solver: Add a hack for external linking
Make it possible to build tests which use
GtkConstraintSolver from inside and outside libgtk.
See the test added in the following commit for
how this is used.
2019-06-28 04:49:42 +00:00
Matthias Clasen ee04ad7e96 demo: Use constraint guide api
Use proper api to create and set up
the guide, and also try max-width.
2019-06-27 22:09:24 +00:00
Matthias Clasen d4f6ad74dd constraint guide: Add more private api
This lets us avoid exposing the struct.
2019-06-27 22:09:24 +00:00
Matthias Clasen 4025b9ed00 constraint guide: Fix the max-size implementation
We need to update all constraints, including
the new ones. Do it with a loop to avoid the
same problem reoccurring.
2019-06-27 22:08:31 +00:00
Matthias Clasen f4fe1f4bcf constraint guide: Add setters/getters
We have properties, so we should have setters
and getters.
2019-06-27 22:08:31 +00:00
Matthias Clasen 75e3017f51 constraint guide: Implement gtk_constraint_guide_new
This was in the headers, but not implemented.
2019-06-27 17:18:30 -04:00
Matthias Clasen f2a604ba9f Split GtkConstraintGuide into its own file
Lest gtkconstraintlayout.c turns too big and messy.
2019-06-27 20:25:42 +00:00
Matthias Clasen a691df2a5f Drop an indirection
This struct is not really useful for just
a single hash table, and it gets in the way
of moving the guide code to its own file.
2019-06-27 19:34:03 +00:00
Matthias Clasen 376b5b326e Give guides max-width/height 2019-06-27 19:13:38 +00:00
Matthias Clasen 38156f4454 Detach guides on unroot
We don't want to leave constraints behind.
2019-06-27 19:10:13 +00:00
Matthias Clasen b462988e3c Simplify the guide implementation
Store the values and constraints in
arrays, to facilitate treating them
uniformly.
2019-06-27 19:10:13 +00:00
Matthias Clasen cf702b81a8 Add an interactive constraints demo 2019-06-27 18:48:04 +01:00
Matthias Clasen 04d7e894e2 Make the constraints demo more interesting
Add a max size to the buttons, to force the
space to open up.
2019-06-27 18:43:53 +01:00
Matthias Clasen 4efcad59df Add GtkConstraintGuide
This is meant to be a flexible space.
2019-06-27 18:43:26 +01:00
Emmanuele Bassi 8bacf47c41 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-27 18:26:19 +01:00
Matthias Clasen 8f5515d498 Redefine constraints with GtkConstraintTarget
This is in preparation for allowing non-widgets
to act as constraint targets.
2019-06-27 18:21:33 +01:00
Matthias Clasen 3a8c1abbc1 widget: Implement GtkConstraintTarget 2019-06-27 18:21:16 +01:00
Matthias Clasen 02f4e1a5ae Add GtkConstraintTarget
This is an marker interface that we will
use to accept other things that widgets
in constraints.
2019-06-27 18:20:23 +01:00
Emmanuele Bassi 7afac64225 Notify a layout change when adding and removing constraints
Changing the set of constraints should cause a relayout.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi cb11ba4ca0 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-27 17:38:02 +01:00
17 changed files with 1597 additions and 153 deletions
+25 -2
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,20 @@ static void
build_constraints (SimpleGrid *self,
GtkConstraintLayout *manager)
{
GtkConstraintGuide *guide;
guide = gtk_constraint_guide_new ();
gtk_constraint_guide_set_min_size (guide, 10, 10);
gtk_constraint_guide_set_nat_size (guide, 100, 10);
gtk_constraint_guide_set_max_size (guide, 200, 20);
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 +108,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,
+245
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;
}
+1
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>
+1
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',
+85 -51
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)
{
}
+22 -3
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);
+498
View File
@@ -0,0 +1,498 @@
/* gtkconstraintguide.c: Flexible space for constraints
* Copyright 2019 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include "gtkconstraintguide.h"
#include "gtkconstraintguideprivate.h"
#include "gtkconstraintlayoutprivate.h"
#include "gtkconstraintexpressionprivate.h"
#include "gtkconstraintsolverprivate.h"
#include "gtkdebug.h"
#include "gtkintl.h"
#include "gtkprivate.h"
typedef enum {
GUIDE_MIN_WIDTH,
GUIDE_MIN_HEIGHT,
GUIDE_NAT_WIDTH,
GUIDE_NAT_HEIGHT,
GUIDE_MAX_WIDTH,
GUIDE_MAX_HEIGHT,
LAST_GUIDE_VALUE
} GuideValue;
struct _GtkConstraintGuide
{
GObject parent_instance;
int values[LAST_GUIDE_VALUE];
GtkConstraintLayout *layout;
/* 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;
GtkConstraintRef *constraints[LAST_GUIDE_VALUE];
};
struct _GtkConstraintGuideClass {
GObjectClass parent_class;
};
enum {
GUIDE_PROP_MIN_WIDTH = 1,
GUIDE_PROP_MIN_HEIGHT,
GUIDE_PROP_NAT_WIDTH,
GUIDE_PROP_NAT_HEIGHT,
GUIDE_PROP_MAX_WIDTH,
GUIDE_PROP_MAX_HEIGHT,
LAST_GUIDE_PROP
};
static GParamSpec *guide_props[LAST_GUIDE_PROP];
static void
gtk_constraint_guide_constraint_target_iface_init (GtkConstraintTargetInterface *iface)
{
}
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->bound_attributes =
g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify) gtk_constraint_variable_unref);
}
static void
gtk_constraint_guide_update_constraint (GtkConstraintGuide *guide,
GuideValue index)
{
GtkConstraintSolver *solver;
GtkConstraintVariable *var;
int attr[LAST_GUIDE_VALUE] = {
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
};
int relation[LAST_GUIDE_VALUE] = {
GTK_CONSTRAINT_RELATION_GE,
GTK_CONSTRAINT_RELATION_GE,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_RELATION_LE,
GTK_CONSTRAINT_RELATION_LE,
};
double weight[LAST_GUIDE_VALUE] = {
GTK_CONSTRAINT_WEIGHT_REQUIRED,
GTK_CONSTRAINT_WEIGHT_REQUIRED,
GTK_CONSTRAINT_WEIGHT_MEDIUM,
GTK_CONSTRAINT_WEIGHT_MEDIUM,
GTK_CONSTRAINT_WEIGHT_REQUIRED,
GTK_CONSTRAINT_WEIGHT_REQUIRED,
};
if (!guide->layout)
return;
solver = gtk_constraint_layout_get_solver (guide->layout);
if (!solver)
return;
if (guide->constraints[index] != NULL)
gtk_constraint_solver_remove_constraint (solver, guide->constraints[index]);
var = gtk_constraint_layout_get_attribute (guide->layout, attr[index], "guide", NULL, guide->bound_attributes);
guide->constraints[index] =
gtk_constraint_solver_add_constraint (solver,
var,
relation[index],
gtk_constraint_expression_new (guide->values[index]),
weight[index]);
}
void
gtk_constraint_guide_update (GtkConstraintGuide *guide)
{
int i;
for (i = 0; i < LAST_GUIDE_VALUE; i++)
gtk_constraint_guide_update_constraint (guide, i);
}
void
gtk_constraint_guide_detach (GtkConstraintGuide *guide)
{
GtkConstraintSolver *solver;
int i;
if (!guide->layout)
return;
solver = gtk_constraint_layout_get_solver (guide->layout);
if (!solver)
return;
for (i = 0; i < LAST_GUIDE_VALUE; i++)
{
gtk_constraint_solver_remove_constraint (solver, guide->constraints[i]);
guide->constraints[i] = NULL;
}
g_hash_table_remove_all (guide->bound_attributes);
}
GtkConstraintVariable *
gtk_constraint_guide_get_attribute (GtkConstraintGuide *guide,
GtkConstraintAttribute attr)
{
GtkLayoutManager *manager = GTK_LAYOUT_MANAGER (guide->layout);
GtkWidget *widget = gtk_layout_manager_get_widget (manager);
return gtk_constraint_layout_get_attribute (guide->layout, attr, "guide", widget, guide->bound_attributes);
}
GtkConstraintLayout *
gtk_constraint_guide_get_layout (GtkConstraintGuide *guide)
{
return guide->layout;
}
void
gtk_constraint_guide_set_layout (GtkConstraintGuide *guide,
GtkConstraintLayout *layout)
{
guide->layout = layout;
}
static void
gtk_constraint_guide_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkConstraintGuide *self = GTK_CONSTRAINT_GUIDE (gobject);
int val;
GuideValue index;
switch (prop_id)
{
case GUIDE_PROP_MIN_WIDTH:
case GUIDE_PROP_MIN_HEIGHT:
case GUIDE_PROP_NAT_WIDTH:
case GUIDE_PROP_NAT_HEIGHT:
case GUIDE_PROP_MAX_WIDTH:
case GUIDE_PROP_MAX_HEIGHT:
val = g_value_get_int (value);
index = prop_id - 1;
if (self->values[index] != val)
{
self->values[index] = val;
g_object_notify_by_pspec (gobject, pspec);
gtk_constraint_guide_update_constraint (self, index);
}
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 GUIDE_PROP_MIN_WIDTH:
case GUIDE_PROP_MIN_HEIGHT:
case GUIDE_PROP_NAT_WIDTH:
case GUIDE_PROP_NAT_HEIGHT:
case GUIDE_PROP_MAX_WIDTH:
case GUIDE_PROP_MAX_HEIGHT:
g_value_set_int (value, self->values[prop_id - 1]);
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);
g_clear_pointer (&self->bound_attributes, g_hash_table_unref);
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[GUIDE_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[GUIDE_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[GUIDE_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[GUIDE_PROP_NAT_HEIGHT] =
g_param_spec_int ("nat-height",
"Natural height",
"Natural height",
0, G_MAXINT, 0,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[GUIDE_PROP_MAX_WIDTH] =
g_param_spec_int ("max-width",
"Maximum width",
"Maximum width",
0, G_MAXINT, G_MAXINT,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[GUIDE_PROP_MAX_HEIGHT] =
g_param_spec_int ("max-height",
"Maximum height",
"Maximum height",
0, G_MAXINT, G_MAXINT,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, LAST_GUIDE_PROP, guide_props);
}
/**
* gtk_constraint_guide_new:
*
* Creates a new #GtkConstraintGuide object.
*
* Return: a new #GtkConstraintGuide object.
*/
GtkConstraintGuide *
gtk_constraint_guide_new (void)
{
return g_object_new (GTK_TYPE_CONSTRAINT_GUIDE, NULL);
}
/**
* gtk_constraint_guide_set_min_size:
* @guide: a #GtkConstraintGuide object
* @width: the new minimum width, or -1 to not change it
* @height: the new minimum height, or -1 to not change it
*
* Sets the minimum size of @guide.
*
* If @guide is attached to a #GtkConstraintLayout,
* the constraints will be updated to reflect the new size.
*/
void
gtk_constraint_guide_set_min_size (GtkConstraintGuide *guide,
int width,
int height)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
g_return_if_fail (width >= -1);
g_return_if_fail (height >= -1);
g_object_freeze_notify (G_OBJECT (guide));
if (width != -1)
g_object_set (guide, "min-width", width, NULL);
if (height != -1)
g_object_set (guide, "min-height", height, NULL);
g_object_thaw_notify (G_OBJECT (guide));
}
/**
* gtk_constraint_guide_get_min_size:
* @guide: a #GtkContraintGuide object
* @width: (allow-none): return location for the minimum width,
* or %NULL
* @height: (allow-none): return location for the minimum height,
* or %NULL
*
* Gets the minimum size of @guide.
*/
void
gtk_constraint_guide_get_min_size (GtkConstraintGuide *guide,
int *width,
int *height)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
if (width)
*width = guide->values[GUIDE_MIN_WIDTH];
if (height)
*height = guide->values[GUIDE_MIN_HEIGHT];
}
/**
* gtk_constraint_guide_set_nat_size:
* @guide: a #GtkConstraintGuide object
* @width: the new natural width, or -1 to not change it
* @height: the new natural height, or -1 to not change it
*
* Sets the natural size of @guide.
*
* If @guide is attached to a #GtkConstraintLayout,
* the constraints will be updated to reflect the new size.
*/
void
gtk_constraint_guide_set_nat_size (GtkConstraintGuide *guide,
int width,
int height)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
g_return_if_fail (width >= -1);
g_return_if_fail (height >= -1);
g_object_freeze_notify (G_OBJECT (guide));
if (width != -1)
g_object_set (guide, "nat-width", width, NULL);
if (height != -1)
g_object_set (guide, "nat-height", height, NULL);
g_object_thaw_notify (G_OBJECT (guide));
}
/**
* gtk_constraint_guide_get_nat_size:
* @guide: a #GtkContraintGuide object
* @width: (allow-none): return location for the natural width,
* or %NULL
* @height: (allow-none): return location for the natural height,
* or %NULL
*
* Gets the natural size of @guide.
*/
void
gtk_constraint_guide_get_nat_size (GtkConstraintGuide *guide,
int *width,
int *height)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
if (width)
*width = guide->values[GUIDE_NAT_WIDTH];
if (height)
*height = guide->values[GUIDE_NAT_HEIGHT];
}
/**
* gtk_constraint_guide_set_max_size:
* @guide: a #GtkConstraintGuide object
* @width: the new maximum width, or -1 to not change it
* @height: the new maximum height, or -1 to not change it
*
* Sets the maximum size of @guide.
*
* If @guide is attached to a #GtkConstraintLayout,
* the constraints will be updated to reflect the new size.
*/
void
gtk_constraint_guide_set_max_size (GtkConstraintGuide *guide,
int width,
int height)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
g_return_if_fail (width >= -1);
g_return_if_fail (height >= -1);
g_object_freeze_notify (G_OBJECT (guide));
if (width != -1)
g_object_set (guide, "max-width", width, NULL);
if (height != -1)
g_object_set (guide, "max-height", height, NULL);
g_object_thaw_notify (G_OBJECT (guide));
}
/**
* gtk_constraint_guide_get_max_size:
* @guide: a #GtkContraintGuide object
* @width: (allow-none): return location for the maximum width,
* or %NULL
* @height: (allow-none): return location for the maximum height,
* or %NULL
*
* Gets the maximum size of @guide.
*/
void
gtk_constraint_guide_get_max_size (GtkConstraintGuide *guide,
int *width,
int *height)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
if (width)
*width = guide->values[GUIDE_MAX_WIDTH];
if (height)
*height = guide->values[GUIDE_MAX_HEIGHT];
}
+71
View File
@@ -0,0 +1,71 @@
/* gtkconstraintguide.h: Flexible space for constraints
* Copyright 2019 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#pragma once
#include <gtk/gtktypes.h>
#include <gtk/gtkenums.h>
G_BEGIN_DECLS
#define GTK_TYPE_CONSTRAINT_GUIDE (gtk_constraint_guide_get_type ())
/**
* GtkConstraintGuide:
*
* An object that can be added to a #GtkConstraintLayout and be
* used in constraints like a widget, without being drawn.
*
* Guides have a minimum, maximum 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);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_set_min_size (GtkConstraintGuide *guide,
int width,
int height);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_get_min_size (GtkConstraintGuide *guide,
int *width,
int *height);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_set_nat_size (GtkConstraintGuide *guide,
int width,
int height);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_get_nat_size (GtkConstraintGuide *guide,
int *width,
int *height);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_set_max_size (GtkConstraintGuide *guide,
int width,
int height);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_get_max_size (GtkConstraintGuide *guide,
int *width,
int *height);
G_END_DECLS
+38
View File
@@ -0,0 +1,38 @@
/* gtkconstraintguideprivate.h: Constraint between two widgets
* Copyright 2019 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#pragma once
#include "gtkconstraintguide.h"
#include "gtkconstraintlayout.h"
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
void gtk_constraint_guide_update (GtkConstraintGuide *guide);
void gtk_constraint_guide_detach (GtkConstraintGuide *guide);
GtkConstraintVariable *gtk_constraint_guide_get_attribute (GtkConstraintGuide *guide,
GtkConstraintAttribute attr);
GtkConstraintLayout *gtk_constraint_guide_get_layout (GtkConstraintGuide *guide);
void gtk_constraint_guide_set_layout (GtkConstraintGuide *guide,
GtkConstraintLayout *layout);
G_END_DECLS
+174 -88
View File
@@ -60,11 +60,13 @@
#include "config.h"
#include "gtkconstraintlayout.h"
#include "gtkconstraintlayoutprivate.h"
#include "gtkconstraintprivate.h"
#include "gtkconstraintexpressionprivate.h"
#include "gtkconstraintsolverprivate.h"
#include "gtklayoutchild.h"
#include "gtkconstraintguideprivate.h"
#include "gtkdebug.h"
#include "gtkintl.h"
@@ -105,11 +107,14 @@ 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)
static inline GtkConstraintSolver *
GtkConstraintSolver *
gtk_constraint_layout_get_solver (GtkConstraintLayout *self)
{
GtkWidget *widget;
@@ -153,22 +158,21 @@ get_attribute_name (GtkConstraintAttribute attr)
return attribute_names[attr];
}
static GtkConstraintVariable *
get_child_attribute (GtkConstraintLayoutChild *self,
GtkConstraintSolver *solver,
GtkWidget *widget,
GtkConstraintAttribute attr)
static GtkConstraintAttribute
resolve_direction (GtkConstraintAttribute attr,
GtkWidget *widget)
{
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 (widget)
text_dir = gtk_widget_get_direction (widget);
else
text_dir = GTK_TEXT_DIR_LTR;
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
@@ -176,23 +180,35 @@ get_child_attribute (GtkConstraintLayoutChild *self,
}
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;
}
return attr;
}
GtkConstraintVariable *
gtk_constraint_layout_get_attribute (GtkConstraintLayout *layout,
GtkConstraintAttribute attr,
const char *prefix,
GtkWidget *widget,
GHashTable *bound_attributes)
{
const char *attr_name;
GtkConstraintVariable *res;
GtkConstraintSolver *solver = layout->solver;
attr = resolve_direction (attr, widget);
attr_name = get_attribute_name (attr);
res = g_hash_table_lookup (self->bound_attributes, attr_name);
res = g_hash_table_lookup (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);
g_hash_table_insert (self->bound_attributes, (gpointer) attr_name, res);
res = gtk_constraint_solver_create_variable (solver, prefix, attr_name, 0.0);
g_hash_table_insert (bound_attributes, (gpointer) attr_name, res);
/* Some attributes are really constraints computed from other
* attributes, to avoid creating additional constraints from
@@ -207,8 +223,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 = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_LEFT, prefix, widget, bound_attributes);
width = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_WIDTH, prefix, widget, bound_attributes);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, left);
@@ -229,8 +245,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 = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_TOP, prefix, widget, bound_attributes);
height = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT, prefix, widget, bound_attributes);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, top);
@@ -251,8 +267,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 = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_LEFT, prefix, widget, bound_attributes);
width = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_WIDTH, prefix, widget, bound_attributes);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, width);
@@ -275,8 +291,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 = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_TOP, prefix, widget, bound_attributes);
height = gtk_constraint_layout_get_attribute (layout, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT, prefix, widget, bound_attributes);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, height);
@@ -325,6 +341,19 @@ get_child_attribute (GtkConstraintLayoutChild *self,
return res;
}
static GtkConstraintVariable *
get_child_attribute (GtkConstraintLayout *layout,
GtkWidget *widget,
GtkConstraintAttribute attr)
{
GtkConstraintLayoutChild *child_info;
const char *prefix = gtk_widget_get_name (widget);
child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (layout), widget));
return gtk_constraint_layout_get_attribute (layout, attr, prefix, widget, child_info->bound_attributes);
}
static void
gtk_constraint_layout_child_finalize (GObject *gobject)
{
@@ -361,6 +390,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 +583,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 +602,27 @@ 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;
target_attr = get_child_attribute (self, GTK_WIDGET (target), attr);
}
else if (GTK_IS_CONSTRAINT_GUIDE (target))
{
GtkConstraintGuide *guide;
child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), target_widget);
target_attr = get_child_attribute (GTK_CONSTRAINT_LAYOUT_CHILD (child_info),
solver,
target_widget,
attr);
guide = (GtkConstraintGuide*)g_hash_table_lookup (self->guides, target);
target_attr = gtk_constraint_guide_get_attribute (guide, 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 +630,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 +639,25 @@ 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;
source_attr = get_child_attribute (self, GTK_WIDGET (source), attr);
}
else if (GTK_IS_CONSTRAINT_GUIDE (source))
{
GtkConstraintGuide *guide;
child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), source_widget);
source_attr = get_child_attribute (GTK_CONSTRAINT_LAYOUT_CHILD (child_info),
solver,
source_widget,
attr);
guide = (GtkConstraintGuide*)g_hash_table_lookup (self->guides, source);
source_attr = gtk_constraint_guide_get_attribute (guide, 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;
}
@@ -683,7 +717,6 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkConstraintLayoutChild *child_info;
GtkConstraintVariable *width_var, *height_var;
GtkConstraintRef *constraint;
int min_size = 0, nat_size = 0;
@@ -691,17 +724,14 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
if (!gtk_widget_should_layout (child))
continue;
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 (self, child, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
constraint =
gtk_constraint_solver_add_constraint (solver,
@@ -721,8 +751,7 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
break;
case GTK_ORIENTATION_VERTICAL:
height_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
height_var = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
constraint =
gtk_constraint_solver_add_constraint (solver,
@@ -769,15 +798,15 @@ 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);
gtk_constraint_solver_begin_edit (solver);
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);
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_remove_edit_variable (solver, opposite_size);
gtk_constraint_solver_end_edit (solver);
}
GTK_NOTE (LAYOUT,
g_print ("layout %p preferred %s size: %.3f (for opposite size: %d)\n",
@@ -788,11 +817,6 @@ gtk_constraint_layout_measure (GtkLayoutManager *manager,
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);
@@ -869,7 +893,6 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkConstraintLayoutChild *child_info;
GtkConstraintVariable *width_var, *height_var;
GtkRequisition min_req, nat_req;
GtkConstraintRef *constraint;
@@ -877,13 +900,9 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
if (!gtk_widget_should_layout (child))
continue;
child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
gtk_widget_get_preferred_size (child, &min_req, &nat_req);
width_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
width_var = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
constraint =
gtk_constraint_solver_add_constraint (solver,
@@ -901,8 +920,7 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
GTK_CONSTRAINT_WEIGHT_MEDIUM);
g_ptr_array_add (size_constraints, constraint);
height_var = get_child_attribute (child_info, solver, child,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
height_var = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
constraint =
gtk_constraint_solver_add_constraint (solver,
@@ -927,21 +945,18 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
{
GtkConstraintVariable *var_top, *var_left, *var_width, *var_height;
GtkConstraintVariable *var_baseline;
GtkConstraintLayoutChild *child_info;
GtkAllocation child_alloc;
int child_baseline = -1;
if (!gtk_widget_should_layout (child))
continue;
child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
/* Retrieve all the values associated with the child */
var_top = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_TOP);
var_left = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
var_width = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
var_height = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
var_baseline = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_BASELINE);
var_top = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_TOP);
var_left = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
var_width = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
var_height = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
var_baseline = get_child_attribute (self, child, GTK_CONSTRAINT_ATTRIBUTE_BASELINE);
GTK_NOTE (LAYOUT,
g_print ("Allocating child '%s'[%p] with { .x: %g, .y: %g, .w: %g, .h: %g, .b: %g }\n",
@@ -1001,9 +1016,15 @@ 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;
gtk_constraint_guide_update (guide);
}
}
static void
@@ -1021,10 +1042,16 @@ gtk_constraint_layout_unroot (GtkLayoutManager *manager)
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GtkConstraint *constraint = key;
gtk_constraint_detach (constraint);
}
g_hash_table_iter_init (&iter, self->guides);
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GtkConstraintGuide *guide = key;
gtk_constraint_guide_detach (guide);
}
self->solver = NULL;
}
@@ -1057,6 +1084,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 +1111,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 +1133,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 +1155,56 @@ 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));
}
/**
* 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 (gtk_constraint_guide_get_layout (guide) == NULL);
gtk_constraint_guide_set_layout (guide, 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)
{
g_return_if_fail (GTK_IS_CONSTRAINT_LAYOUT (layout));
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
g_return_if_fail (gtk_constraint_guide_get_layout (guide) == layout);
gtk_constraint_guide_detach (guide);
gtk_constraint_guide_set_layout (guide, NULL);
g_hash_table_remove (layout->guides, guide);
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout));
}
+14 -6
View File
@@ -20,12 +20,21 @@
#include <gtk/gtklayoutmanager.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkconstraintguide.h>
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 ())
/**
* GtkConstraintLayoutChild:
*
* A #GtkLayoutChild in a #GtkConstraintLayout.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK, CONSTRAINT_LAYOUT_CHILD, GtkLayoutChild)
/**
* GtkConstraintLayout:
*
@@ -45,12 +54,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
+2 -2
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;
+20
View File
@@ -25,10 +25,30 @@
G_BEGIN_DECLS
#ifdef GTK_TEST_EXTERNAL
#define GTK_TYPE_CONSTRAINT_SOLVER (g_type_from_name ("GtkConstraintSolver"))
#define GTK_CONSTRAINT_SOLVER(solver) (G_TYPE_CHECK_INSTANCE_CAST ((solver), GTK_TYPE_CONSTRAINT_SOLVER, GtkConstraintSolver))
#define GTK_CONSTRAINT_SOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CONSTRAINT_SOLVER, GtkConstraintSolverClass))
#define GTK_IS_CONSTRAINT_SOLVER(solver) (G_TYPE_CHECK_INSTANCE_TYPE ((solver), GTK_TYPE_CONSTRAINT_SOLVER))
#define GTK_IS_CONSTRAINT_SOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CONSTRAINT_SOLVER))
#define GTK_CONSTRAINT_SOLVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CONSTRAINT_SOLVER, GtkConstraintSolverClass))
typedef struct _GtkConstraintSolver GtkConstraintSolver;
typedef struct _GtkConstraintSolverClass GtkConstraintSolverClass;
struct _GtkConstraintSolverClass {
GObjectClass parent_class;
};
#else
#define GTK_TYPE_CONSTRAINT_SOLVER (gtk_constraint_solver_get_type())
G_DECLARE_FINAL_TYPE (GtkConstraintSolver, gtk_constraint_solver, GTK, CONSTRAINT_SOLVER, GObject)
#endif
/* Symbolic weight thresholds
*
* Constraint weights live on a continuum, but we use thresholds for simplicity's
+10
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;
+1
View File
@@ -202,6 +202,7 @@ gtk_public_sources = files([
'gtkcombobox.c',
'gtkcomboboxtext.c',
'gtkcomposetable.c',
'gtkconstraintguide.c',
'gtkconstraintlayout.c',
'gtkconstraint.c',
'gtkcontainer.c',
+381
View File
@@ -0,0 +1,381 @@
#include <gtk/gtk.h>
#include "../../gtk/gtkconstrainttypesprivate.h"
#include "../../gtk/gtkconstraintsolverprivate.h"
#include "../../gtk/gtkconstraintexpressionprivate.h"
typedef struct _Node Node;
static GtkConstraintSolver *solver;
static Node *tree;
static Node *drag_node;
static double drag_start_x;
static double drag_start_y;
static GtkConstraintVariable *width_var;
static GtkConstraintVariable *height_var;
struct _Node {
double x;
double y;
Node *parent;
Node *left;
Node *right;
GtkConstraintVariable *x_var;
GtkConstraintVariable *y_var;
};
static Node *
make_tree (Node *parent,
int depth,
int x,
int y,
int dx,
int dy)
{
Node *node;
node = g_new0 (Node, 1);
node->parent = parent;
if (depth > 0)
{
node->left = make_tree (node, depth - 1, x - dx, y + dy, dx / 2, dy);
node->right = make_tree (node, depth - 1, x + dx, y + dy, dx / 2, dy);
}
node->x = x;
node->y = y;
node->x_var = gtk_constraint_solver_create_variable (solver, NULL, "x", x);
node->y_var = gtk_constraint_solver_create_variable (solver, NULL, "y", y);
/* weak stay for the current position */
gtk_constraint_solver_add_stay_variable (solver, node->x_var, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_stay_variable (solver, node->y_var, GTK_CONSTRAINT_WEIGHT_WEAK);
/* require to stay in area */
gtk_constraint_solver_add_constraint (solver,
node->x_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (0.0),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_add_constraint (solver,
node->x_var,
GTK_CONSTRAINT_RELATION_LE,
gtk_constraint_expression_new (1600.0),
//gtk_constraint_expression_new_from_variable (width_var),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_add_constraint (solver,
node->y_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (0.0),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_add_constraint (solver,
node->y_var,
GTK_CONSTRAINT_RELATION_LE,
gtk_constraint_expression_new (600.0),
//gtk_constraint_expression_new_from_variable (height_var),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
if (node->left)
{
GtkConstraintExpressionBuilder builder;
/* left.y = right.y */
gtk_constraint_solver_add_constraint (solver,
node->left->y_var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new_from_variable (node->right->y_var),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
/* left.y >= parent.y + 10 */
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, node->y_var);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_constant (&builder, 10.0);
gtk_constraint_solver_add_constraint (solver,
node->left->y_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_builder_finish (&builder),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
/* right.y >= parent.y + 10 */
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, node->y_var);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_constant (&builder, 10.0);
gtk_constraint_solver_add_constraint (solver,
node->right->y_var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_builder_finish (&builder),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
/* parent.x = (left.x + right.x) / 2 */
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, node->left->x_var);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_term (&builder, node->right->x_var);
gtk_constraint_expression_builder_divide_by (&builder);
gtk_constraint_expression_builder_constant (&builder, 2.0);
gtk_constraint_solver_add_constraint (solver,
node->x_var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_builder_finish (&builder),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
}
return node;
}
static void
draw_node (Node *node, cairo_t *cr)
{
if (node->left)
draw_node (node->left, cr);
if (node->right)
draw_node (node->right, cr);
if (node->parent)
{
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_move_to (cr, node->parent->x, node->parent->y);
cairo_line_to (cr, node->x, node->y);
cairo_stroke (cr);
}
if (node == drag_node)
cairo_set_source_rgb (cr, 1, 0, 0);
else
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_move_to (cr, node->x, node->y);
cairo_arc (cr, node->x, node->y, 5, 0, 2*M_PI);
cairo_close_path (cr);
cairo_fill (cr);
}
static void
draw_func (GtkDrawingArea *da,
cairo_t *cr,
int width,
int height,
gpointer data)
{
cairo_set_line_width (cr, 1);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_paint (cr);
draw_node (tree, cr);
}
static Node *
find_node (Node *node,
double x,
double y)
{
Node *ret;
double dx = x - node->x;
double dy = y - node->y;
if (dx*dx + dy*dy < 10*10)
return node;
if (node->left)
{
ret = find_node (node->left, x, y);
if (ret)
return ret;
}
if (node->right)
{
ret = find_node (node->right, x, y);
if (ret)
return ret;
}
return NULL;
}
static void
drag_begin (GtkGestureDrag *drag,
double start_x,
double start_y,
gpointer data)
{
drag_node = find_node (tree, start_x, start_y);
if (!drag_node)
return;
drag_start_x = start_x;
drag_start_y = start_y;
gtk_widget_queue_draw (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag)));
}
static void
update_tree (Node *node)
{
if (!node)
return;
node->x = gtk_constraint_variable_get_value (node->x_var);
node->y = gtk_constraint_variable_get_value (node->y_var);
update_tree (node->left);
update_tree (node->right);
}
static void
drag_update (GtkGestureDrag *drag,
double offset_x,
double offset_y,
gpointer data)
{
if (!drag_node)
return;
gtk_constraint_solver_add_edit_variable (solver,
drag_node->x_var,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_add_edit_variable (solver,
drag_node->y_var,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_begin_edit (solver);
gtk_constraint_solver_suggest_value (solver,
drag_node->x_var,
drag_start_x + offset_x);
gtk_constraint_solver_suggest_value (solver,
drag_node->y_var,
drag_start_y + offset_y);
gtk_constraint_solver_resolve (solver);
update_tree (tree);
gtk_constraint_solver_remove_edit_variable (solver, drag_node->x_var);
gtk_constraint_solver_remove_edit_variable (solver, drag_node->y_var);
gtk_constraint_solver_end_edit (solver);
gtk_widget_queue_draw (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag)));
}
static void
drag_end (GtkGestureDrag *drag,
double offset_x,
double offset_y,
gpointer data)
{
if (!drag_node)
return;
drag_node = NULL;
gtk_widget_queue_draw (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag)));
}
static void
size_change (GtkWidget *da,
int width,
int height,
int baseline,
gpointer data)
{
gtk_constraint_variable_set_value (width_var, width);
gtk_constraint_variable_set_value (height_var, height);
gtk_constraint_solver_resolve (solver);
}
static void
reset_tree (Node *node,
int x,
int y,
int dx,
int dy)
{
node->x = x;
node->y = y;
gtk_constraint_solver_remove_stay_variable (solver, node->x_var);
gtk_constraint_solver_remove_stay_variable (solver, node->y_var);
gtk_constraint_variable_set_value (node->x_var, x);
gtk_constraint_variable_set_value (node->y_var, y);
gtk_constraint_solver_add_stay_variable (solver, node->x_var, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_stay_variable (solver, node->y_var, GTK_CONSTRAINT_WEIGHT_WEAK);
if (node->left)
reset_tree (node->left, x - dx, y + dy, dx / 2, dy);
if (node->right)
reset_tree (node->right, x + dx, y + dy, dx / 2, dy);
}
static void
reset (GtkButton *button,
GtkWidget *da)
{
int width, height;
width = gtk_widget_get_allocated_width (da);
height = gtk_widget_get_allocated_height (da);
gtk_constraint_solver_freeze (solver);
reset_tree (tree, width / 2, 20, width / 4 - 40, (height - 40) / 7);
gtk_constraint_solver_thaw (solver);
gtk_widget_queue_draw (da);
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *header;
GtkWidget *button;
GtkWidget *da;
GtkGesture *drag;
int width = 1600;
int height = 600;
gtk_init ();
da = gtk_drawing_area_new ();
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
header = gtk_header_bar_new ();
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
button = gtk_button_new_with_label ("Reset");
g_signal_connect (button, "clicked", G_CALLBACK (reset), da);
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (da), width);
gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (da), height);
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), da);
drag = gtk_gesture_drag_new ();
g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL);
g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), NULL);
g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), NULL);
gtk_widget_add_controller (da, GTK_EVENT_CONTROLLER (drag));
solver = g_object_new (g_type_from_name ("GtkConstraintSolver"), NULL);
gtk_constraint_solver_freeze (solver);
width_var = gtk_constraint_solver_create_variable (solver, NULL, "width", width);
height_var = gtk_constraint_solver_create_variable (solver, NULL, "height", height);
gtk_constraint_solver_add_stay_variable (solver, width_var, GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_add_stay_variable (solver, height_var, GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_signal_connect (da, "size-allocate", G_CALLBACK (size_change), NULL);
tree = make_tree (NULL, 7, width / 2, 20, width / 4 - 40, (height - 40) / 7);
gtk_constraint_solver_thaw (solver);
gtk_widget_show (window);
gtk_main ();
return 0;
}
+9 -1
View File
@@ -1,5 +1,10 @@
gtk_tests = [
# testname, optional extra sources
['constrainttree', [
'../gtk/gtkconstraintsolver.c',
'../gtk/gtkconstraintexpression.c',
], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG', '-DGTK_TEST_EXTERNAL']
],
['rendernode'],
['rendernode-create-tests'],
['overlayscroll'],
@@ -142,9 +147,12 @@ test_args = ['-DGTK_SRCDIR="@0@"'.format(meson.current_source_dir())]
foreach t: gtk_tests
test_name = t.get(0)
test_srcs = ['@0@.c'.format(test_name), t.get(1, [])]
test_extra_cargs = t.get(2, [])
test_extra_ldflags = t.get(3, [])
executable(test_name, test_srcs,
include_directories: [confinc, gdkinc],
c_args: test_args,
c_args: test_args + test_extra_cargs,
link_args : test_extra_ldflags,
dependencies: [libgtk_dep, libm])
endforeach