Compare commits

...

6 Commits

Author SHA1 Message Date
Matthias Clasen
8fcb4a0344 Add a larger constraints demo
Currently, this mainly demonstrates the scalability
limits of the constraints solver. For comparison,
the same demo can be run with the grid layout.
2019-06-27 17:21:34 +00:00
Matthias Clasen
170ce886c9 Add a constraint grid demo
This shows a cross made from overlapping grids.
2019-06-27 17:06:32 +00:00
Matthias Clasen
68f6fe7732 Rename the constraints demo
We're going to add another one, so
move them a level down in the tree.
2019-06-27 17:06:32 +00:00
Matthias Clasen
5e8d5dafc1 Add a constraint-based grid
This is implemented as a auxiliary object that
generates a bunch of constraints (and variables)
when added to a constraint layout.

Maybe this could be generalized to a 'constraint
set' concept, if we come up with other layouts
that we want to reimplement in this way.
2019-06-27 17:04:41 +00: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
13 changed files with 2423 additions and 17 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

View File

@@ -0,0 +1,190 @@
/* Constraints/Grid
*
* GtkConstraintLayout lets you define complex layouts
* like grids.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE (ComplexGrid, complex_grid, COMPLEX, GRID, GtkWidget)
struct _ComplexGrid
{
GtkWidget parent_instance;
GtkWidget *button1, *button2, *button3;
GtkWidget *button4, *button5;
};
G_DEFINE_TYPE (ComplexGrid, complex_grid, GTK_TYPE_WIDGET)
static void
complex_grid_destroy (GtkWidget *widget)
{
ComplexGrid *self = COMPLEX_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);
g_clear_pointer (&self->button4, gtk_widget_destroy);
g_clear_pointer (&self->button5, gtk_widget_destroy);
GTK_WIDGET_CLASS (complex_grid_parent_class)->destroy (widget);
}
static void
complex_grid_class_init (ComplexGridClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->destroy = complex_grid_destroy;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
/* Layout:
*
* +--------------------------------------+
* | +-----------+ |
* | | Child 4 | |
* | +-----------+-----------+----------+ |
* | | Child 1 | Child 2 | Child 3 | |
* | +-----------+-----------+----------+ |
* | | Child 5 | |
* | +-----------+ |
* +--------------------------------------+
*
*/
static void
build_constraints (ComplexGrid *self,
GtkConstraintLayout *manager)
{
GtkGridConstraint *constraint;
GtkConstraint *s;
s = gtk_constraint_new (NULL, GTK_CONSTRAINT_ATTRIBUTE_LEFT,
GTK_CONSTRAINT_RELATION_EQ,
self->button1, GTK_CONSTRAINT_ATTRIBUTE_LEFT,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (manager, s);
s = gtk_constraint_new (self->button3, GTK_CONSTRAINT_ATTRIBUTE_RIGHT,
GTK_CONSTRAINT_RELATION_EQ,
NULL, GTK_CONSTRAINT_ATTRIBUTE_RIGHT,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (manager, s);
s = gtk_constraint_new (NULL, GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
self->button4, GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (manager, s);
s = gtk_constraint_new (NULL, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
self->button5, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (manager, s);
constraint = gtk_grid_constraint_new ();
g_object_set (constraint, "column-homogeneous", TRUE, NULL);
gtk_grid_constraint_add (constraint,
self->button1,
0, 1, 0, 1);
gtk_grid_constraint_add (constraint,
self->button2,
1, 2, 0, 1);
gtk_grid_constraint_add (constraint,
self->button3,
2, 3, 0, 1);
gtk_constraint_layout_add_grid_constraint (manager, constraint);
constraint = gtk_grid_constraint_new ();
g_object_set (constraint, "row-homogeneous", TRUE, NULL);
gtk_grid_constraint_add (constraint,
self->button4,
0, 1, 0, 1);
gtk_grid_constraint_add (constraint,
self->button2,
0, 1, 1, 2);
gtk_grid_constraint_add (constraint,
self->button5,
0, 1, 2, 3);
gtk_constraint_layout_add_grid_constraint (manager, constraint);
}
static void
complex_grid_init (ComplexGrid *self)
{
GtkWidget *widget = GTK_WIDGET (self);
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
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");
self->button4 = gtk_button_new_with_label ("Child 4");
gtk_widget_set_parent (self->button4, widget);
gtk_widget_set_name (self->button4, "button4");
self->button5 = gtk_button_new_with_label ("Child 5");
gtk_widget_set_parent (self->button5, widget);
gtk_widget_set_name (self->button5, "button5");
build_constraints (self, GTK_CONSTRAINT_LAYOUT (manager));
}
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 (complex_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

@@ -0,0 +1,355 @@
/* Constraints/Words
*
* GtkConstraintLayout lets you define big grids.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#define WORDS_TYPE_BASE (words_base_get_type ())
#define WORDS_TYPE_GRID (words_grid_get_type ())
#define WORDS_TYPE_CONSTRAINT (words_constraint_get_type ())
typedef struct
{
GtkWidget parent_instance;
} WordsBase;
typedef WordsBase WordsGrid;
typedef WordsBase WordsConstraint;
typedef GtkWidgetClass WordsBaseClass;
typedef GtkWidgetClass WordsGridClass;
typedef GtkWidgetClass WordsConstraintClass;
G_DEFINE_TYPE (WordsBase, words_base, GTK_TYPE_WIDGET)
G_DEFINE_TYPE (WordsGrid, words_grid, WORDS_TYPE_BASE)
G_DEFINE_TYPE (WordsConstraint, words_constraint, WORDS_TYPE_BASE)
static void
words_grid_init (WordsGrid *words)
{
}
static void
words_grid_class_init (WordsGridClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_GRID_LAYOUT);
}
static void
words_constraint_init (WordsGrid *words)
{
}
static void
words_constraint_class_init (WordsConstraintClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
static void
word_base_dispose (GObject *object)
{
GtkWidget *self = GTK_WIDGET (object);
GtkWidget *child;
while ((child = gtk_widget_get_first_child (self)) != NULL)
gtk_widget_unparent (child);
G_OBJECT_CLASS (words_base_parent_class)->dispose (object);
}
static void
words_base_class_init (WordsBaseClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = word_base_dispose;
}
static int num_words = 100;
static gboolean use_constraints = FALSE;
static void
read_words (WordsBase *self)
{
GBytes *data;
const char *words;
int left, top;
GtkWidget *child = NULL;
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
GtkGridConstraint *grid;
GtkConstraint *constraint;
int count;
int rightmost;
GtkWidget *right_child = NULL;
gboolean use_constraint = GTK_IS_CONSTRAINT_LAYOUT (layout);
if (use_constraint)
{
grid = gtk_grid_constraint_new ();
g_object_set (grid,
"row-homogeneous", TRUE,
"column-homogeneous", FALSE,
NULL);
}
else
{
gtk_grid_layout_set_row_homogeneous (GTK_GRID_LAYOUT (layout), TRUE);
gtk_grid_layout_set_column_homogeneous (GTK_GRID_LAYOUT (layout), FALSE);
}
data = g_resources_lookup_data ("/constraints3/words", 0, NULL);
words = g_bytes_get_data (data, NULL);
count = 0;
rightmost = 0;
left = 0;
top = 0;
while (words && words[0])
{
char *p = strchr (words, '\n');
char *word;
int len;
if (p)
{
word = strndup (words, p - words);
words = p + 1;
}
else
{
word = strdup (words);
words = NULL;
}
len = strlen (word);
child = gtk_button_new_with_label (word);
if (left + len > 50)
{
top++;
left = 0;
}
gtk_widget_set_parent (child, GTK_WIDGET (self));
if (left + len > rightmost)
{
rightmost = left + len;
right_child = child;
}
if (use_constraint)
{
gtk_grid_constraint_add (grid, child,
left, left + len,
top, top + 1);
if (left == 0 && top == 0)
{
constraint = gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
child,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (layout),
constraint);
constraint = gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
GTK_CONSTRAINT_RELATION_EQ,
child,
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (layout),
constraint);
}
}
else
{
GtkGridLayoutChild *grid_child = GTK_GRID_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (layout, child));
g_object_set (grid_child,
"left-attach", left,
"top-attach", top,
"column-span", len,
"row-span", 1,
NULL);
}
left = left + len;
count++;
if (count >= num_words)
break;
}
if (use_constraint)
{
constraint = gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_RIGHT,
GTK_CONSTRAINT_RELATION_EQ,
right_child,
GTK_CONSTRAINT_ATTRIBUTE_RIGHT,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (layout),
constraint);
constraint = gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
child,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (layout),
constraint);
gtk_constraint_layout_add_grid_constraint (GTK_CONSTRAINT_LAYOUT (layout),
grid);
}
g_bytes_unref (data);
}
static void
words_base_init (WordsBase *self)
{
read_words (self);
}
static void
show_words (GtkWidget *parent)
{
GtkWidget *window;
GtkWidget *header, *box, *grid, *button;
GtkWidget *swin;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_transient_for (GTK_WINDOW (window),
GTK_WINDOW (gtk_widget_get_root (parent)));
gtk_window_set_modal (GTK_WINDOW (window), TRUE);
header = gtk_header_bar_new ();
gtk_header_bar_set_title (GTK_HEADER_BAR (header), use_constraints ? "Constraints" : "Grid");
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), FALSE);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (window), box);
swin = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_propagate_natural_width (GTK_SCROLLED_WINDOW (swin), TRUE);
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (swin), TRUE);
gtk_widget_set_hexpand (swin, TRUE);
gtk_widget_set_vexpand (swin, TRUE);
gtk_widget_set_halign (swin, GTK_ALIGN_FILL);
gtk_widget_set_valign (swin, GTK_ALIGN_FILL);
gtk_container_add (GTK_CONTAINER (box), swin);
if (use_constraints)
grid = g_object_new (WORDS_TYPE_CONSTRAINT, NULL);
else
grid = g_object_new (WORDS_TYPE_GRID, NULL);
gtk_widget_set_halign (swin, GTK_ALIGN_START);
gtk_widget_set_valign (swin, GTK_ALIGN_START);
gtk_container_add (GTK_CONTAINER (swin), grid);
button = gtk_button_new_with_label ("Close");
gtk_container_add (GTK_CONTAINER (box), button);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (gtk_widget_destroy), window);
gtk_widget_show (window);
}
static void
use_constraints_cb (GtkButton *button)
{
use_constraints = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
}
static void
word_count_cb (GtkSpinButton *button)
{
num_words = gtk_spin_button_get_value_as_int (button);
}
GtkWidget *
do_constraints3 (GtkWidget *do_widget)
{
static GtkWidget *window;
if (!window)
{
GtkWidget *header, *grid, *button, *label;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
header = gtk_header_bar_new ();
gtk_header_bar_set_title (GTK_HEADER_BAR (header), "Words");
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
grid = gtk_grid_new ();
g_object_set (grid,
"margin", 12,
"row-spacing", 12,
"column-spacing", 6,
"halign", GTK_ALIGN_FILL,
"valign", GTK_ALIGN_FILL,
"hexpand", TRUE,
"vexpand", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (window), grid);
label = gtk_label_new ("Constraints:");
gtk_label_set_xalign (GTK_LABEL (label), 1.0);
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
button = gtk_check_button_new ();
g_signal_connect (button, "clicked", G_CALLBACK (use_constraints_cb), NULL);
gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 1);
label = gtk_label_new ("Words:");
gtk_label_set_xalign (GTK_LABEL (label), 1.0);
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
button = gtk_spin_button_new_with_range (0, 1300, 1);
g_signal_connect (button, "value-changed", G_CALLBACK (word_count_cb), NULL);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (button), 10);
gtk_grid_attach (GTK_GRID (grid), button, 1, 1, 1, 1);
button = gtk_button_new_with_label ("Show");
gtk_widget_set_halign (button, GTK_ALIGN_END);
gtk_widget_set_valign (button, GTK_ALIGN_END);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (show_words), window);
gtk_grid_attach (GTK_GRID (grid), button, 0, 2, 2, 1);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -12,6 +12,9 @@
<gresource prefix="/builder">
<file>demo.ui</file>
</gresource>
<gresource prefix="/constraints3">
<file>words</file>
</gresource>
<gresource prefix="/css_accordion">
<file>css_accordion.css</file>
<file>reset.css</file>
@@ -151,6 +154,8 @@
<file>colorsel.c</file>
<file>combobox.c</file>
<file>constraints.c</file>
<file>constraints2.c</file>
<file>constraints3.c</file>
<file>css_accordion.c</file>
<file>css_basics.c</file>
<file>css_blendmodes.c</file>

View File

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

1315
demos/gtk-demo/words Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -132,6 +132,7 @@
#include <gtk/gtkgesturezoom.h>
#include <gtk/gtkglarea.h>
#include <gtk/gtkgrid.h>
#include <gtk/gtkgridconstraint.h>
#include <gtk/gtkgridlayout.h>
#include <gtk/gtkheaderbar.h>
#include <gtk/gtkicontheme.h>

View File

@@ -62,6 +62,7 @@
#include "gtkconstraintlayout.h"
#include "gtkconstraintprivate.h"
#include "gtkgridconstraintprivate.h"
#include "gtkconstraintexpressionprivate.h"
#include "gtkconstraintsolverprivate.h"
#include "gtklayoutchild.h"
@@ -105,6 +106,8 @@ struct _GtkConstraintLayout
* parent widget, using the public API objects.
*/
GHashTable *constraints;
GHashTable *grid_constraints;
};
G_DEFINE_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK_TYPE_LAYOUT_CHILD)
@@ -361,6 +364,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->grid_constraints, g_hash_table_unref);
G_OBJECT_CLASS (gtk_constraint_layout_parent_class)->finalize (gobject);
}
@@ -693,7 +697,7 @@ 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);
@@ -769,15 +773,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 +792,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);
@@ -982,6 +981,9 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
gtk_constraint_solver_remove_constraint (solver, stay_l);
}
static void layout_add_grid_constraint (GtkConstraintLayout *manager,
GtkGridConstraint *constraint);
static void
gtk_constraint_layout_root (GtkLayoutManager *manager)
{
@@ -1001,9 +1003,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->grid_constraints);
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GtkGridConstraint *constraint = key;
layout_add_grid_constraint (self, constraint);
}
}
static void
@@ -1025,6 +1033,14 @@ gtk_constraint_layout_unroot (GtkLayoutManager *manager)
gtk_constraint_detach (constraint);
}
g_hash_table_iter_init (&iter, self->grid_constraints);
while (g_hash_table_iter_next (&iter, &key, NULL))
{
GtkGridConstraint *constraint = key;
gtk_grid_constraint_detach (constraint);
}
self->solver = NULL;
}
@@ -1057,6 +1073,10 @@ gtk_constraint_layout_init (GtkConstraintLayout *self)
g_hash_table_new_full (NULL, NULL,
(GDestroyNotify) g_object_unref,
NULL);
self->grid_constraints =
g_hash_table_new_full (NULL, NULL,
(GDestroyNotify) g_object_unref,
NULL);
}
/**
@@ -1101,6 +1121,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 +1143,212 @@ 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));
}
void
gtk_constraint_layout_add_grid_constraint (GtkConstraintLayout *manager,
GtkGridConstraint *constraint)
{
g_return_if_fail (GTK_IS_CONSTRAINT_LAYOUT (manager));
g_return_if_fail (GTK_IS_GRID_CONSTRAINT (constraint));
g_return_if_fail (!gtk_grid_constraint_is_attached (constraint));
layout_add_grid_constraint (manager, constraint);
g_hash_table_add (manager->grid_constraints, constraint);
}
static GtkConstraintVariable **
allocate_variables (GtkConstraintSolver *solver,
const char *name,
int n)
{
GtkConstraintVariable **vars;
int i;
vars = g_new (GtkConstraintVariable *, n);
for (i = 0; i < n; i++)
{
char *vname = g_strdup_printf ("%s%d", name, i);
vars[i] = gtk_constraint_solver_create_variable (solver, NULL, vname, 0.0);
}
return vars;
}
#if 0
static void
add_ordering_constraints (GtkConstraintSolver *solver,
GtkConstraintVariable **vars,
int n_vars,
GPtrArray *refs)
{
int i;
for (i = 1; i < n_vars; i++)
{
GtkConstraintRef *ref;
ref = gtk_constraint_solver_add_constraint (solver,
vars[i],
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new_from_variable (vars[i - 1]),
GTK_CONSTRAINT_WEIGHT_MEDIUM);
g_ptr_array_add (refs, ref);
}
}
#endif
static void
add_homogeneous_constraints (GtkConstraintSolver *solver,
GtkConstraintVariable **vars,
int n_vars,
GPtrArray *refs)
{
int i;
for (i = 2; i < n_vars; i++)
{
GtkConstraintExpressionBuilder builder;
GtkConstraintRef *ref;
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, vars[i]);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_term (&builder, vars[i - 2]);
gtk_constraint_expression_builder_divide_by (&builder);
gtk_constraint_expression_builder_constant (&builder, 2.0);
ref = gtk_constraint_solver_add_constraint (solver,
vars[i - 1],
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_builder_finish (&builder),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (refs, ref);
}
}
static void
add_child_constraints (GtkConstraintLayout *manager,
GtkConstraintSolver *solver,
GtkGridConstraintChild *child,
GtkConstraintVariable **rows,
GtkConstraintVariable **cols,
GPtrArray *refs)
{
GtkConstraintLayoutChild *info;
GtkConstraintVariable *var;
GtkConstraintVariable *var1;
GtkConstraintRef *ref;
info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (manager), child->child));
var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
var1 = cols[child->left];
ref = gtk_constraint_solver_add_constraint (solver,
var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new_from_variable (var1),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (refs, ref);
var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_RIGHT);
var1 = cols[child->right];
ref = gtk_constraint_solver_add_constraint (solver,
var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new_from_variable (var1),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (refs, ref);
var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_TOP);
var1 = rows[child->top];
ref = gtk_constraint_solver_add_constraint (solver,
var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new_from_variable (var1),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (refs, ref);
var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM);
var1 = rows[child->bottom];
ref = gtk_constraint_solver_add_constraint (solver,
var,
GTK_CONSTRAINT_RELATION_EQ,
gtk_constraint_expression_new_from_variable (var1),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_ptr_array_add (refs, ref);
}
static void
layout_add_grid_constraint (GtkConstraintLayout *manager,
GtkGridConstraint *constraint)
{
GtkWidget *layout_widget;
GtkConstraintSolver *solver;
GtkConstraintVariable **rows;
GtkConstraintVariable **cols;
int n_rows, n_cols;
GPtrArray *refs;
int i;
if (gtk_grid_constraint_is_attached (constraint))
return;
layout_widget = gtk_layout_manager_get_widget (GTK_LAYOUT_MANAGER (manager));
if (layout_widget == NULL)
return;
solver = gtk_constraint_layout_get_solver (manager);
if (solver == NULL)
return;
gtk_constraint_solver_freeze (solver);
refs = g_ptr_array_new ();
n_rows = n_cols = 0;
for (i = 0; i < constraint->children->len; i++)
{
GtkGridConstraintChild *child = g_ptr_array_index (constraint->children, i);
n_rows = MAX (n_rows, child->bottom);
n_cols = MAX (n_cols, child->right);
}
n_rows++;
n_cols++;
rows = allocate_variables (solver, "row", n_rows);
cols = allocate_variables (solver, "col", n_cols);
#if 0
//FIXME for some reason, these 'obvious' constraints
// make things unstable (and they are not really needed)
add_ordering_constraints (solver, rows, n_rows, refs);
add_ordering_constraints (solver, cols, n_cols, refs);
#endif
if (constraint->row_homogeneous)
add_homogeneous_constraints (solver, rows, n_rows, refs);
if (constraint->column_homogeneous)
add_homogeneous_constraints (solver, cols, n_cols, refs);
for (i = 0; i < constraint->children->len; i++)
{
GtkGridConstraintChild *child = g_ptr_array_index (constraint->children, i);
add_child_constraints (manager, solver, child, rows, cols, refs);
}
gtk_grid_constraint_attach (constraint, solver, refs);
g_free (rows);
g_free (cols);
g_ptr_array_unref (refs);
gtk_constraint_solver_thaw (solver);
}

View File

@@ -20,6 +20,7 @@
#include <gtk/gtklayoutmanager.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkgridconstraint.h>
G_BEGIN_DECLS
@@ -45,6 +46,10 @@ GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_remove_constraint (GtkConstraintLayout *manager,
GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_add_grid_constraint (GtkConstraintLayout *manager,
GtkGridConstraint *constraint);
/**
* GtkConstraintLayoutChild:
*

206
gtk/gtkgridconstraint.c Normal file
View File

@@ -0,0 +1,206 @@
/* gtkgridconstraint.c: Make a grid with 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 "gtkgridconstraint.h"
#include "gtkgridconstraintprivate.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
enum {
PROP_ROW_HOMOGENEOUS = 1,
PROP_COLUMN_HOMOGENEOUS,
N_PROPERTIES
};
static GParamSpec *obj_props[N_PROPERTIES];
G_DEFINE_TYPE (GtkGridConstraint, gtk_grid_constraint, G_TYPE_OBJECT)
static void
gtk_constraint_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
switch (prop_id)
{
case PROP_ROW_HOMOGENEOUS:
self->row_homogeneous = g_value_get_boolean (value);
break;
case PROP_COLUMN_HOMOGENEOUS:
self->column_homogeneous = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_constraint_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
switch (prop_id)
{
case PROP_ROW_HOMOGENEOUS:
g_value_set_boolean (value, self->row_homogeneous);
break;
case PROP_COLUMN_HOMOGENEOUS:
g_value_set_boolean (value, self->column_homogeneous);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_constraint_finalize (GObject *gobject)
{
GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
gtk_grid_constraint_detach (self);
g_ptr_array_free (self->children, TRUE);
G_OBJECT_CLASS (gtk_grid_constraint_parent_class)->finalize (gobject);
}
static void
gtk_grid_constraint_class_init (GtkGridConstraintClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gtk_constraint_set_property;
gobject_class->get_property = gtk_constraint_get_property;
gobject_class->finalize = gtk_constraint_finalize;
/**
* GtkGridConstraint:row-homogeneous:
*
* Whether to make all rows the same height.
*/
obj_props[PROP_ROW_HOMOGENEOUS] =
g_param_spec_boolean ("row-homogeneous",
P_("Row homogeneous"),
P_("Row homogeneous"),
FALSE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
/**
* GtkGridConstraint:column-homogeneous:
*
* Whether to make all columns the same width.
*/
obj_props[PROP_COLUMN_HOMOGENEOUS] =
g_param_spec_boolean ("column-homogeneous",
P_("Column homogeneous"),
P_("Column homogeneous"),
FALSE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_props);
}
static void
gtk_grid_constraint_init (GtkGridConstraint *self)
{
self->children = g_ptr_array_new_with_free_func (g_free);
}
GtkGridConstraint *
gtk_grid_constraint_new (void)
{
return g_object_new (GTK_TYPE_GRID_CONSTRAINT, NULL);
}
void
gtk_grid_constraint_add (GtkGridConstraint *self,
GtkWidget *child,
int left,
int right,
int top,
int bottom)
{
GtkGridConstraintChild *data;
g_return_if_fail (GTK_IS_GRID_CONSTRAINT (self));
g_return_if_fail (GTK_IS_WIDGET (child));
g_return_if_fail (left < right);
g_return_if_fail (top < bottom);
g_return_if_fail (self->refs == NULL);
data = g_new0 (GtkGridConstraintChild, 1);
data->child = child;
data->left = left;
data->right = right;
data->top = top;
data->bottom = bottom;
g_ptr_array_add (self->children, data);
}
gboolean
gtk_grid_constraint_is_attached (GtkGridConstraint *self)
{
return self->refs != NULL;
}
void
gtk_grid_constraint_attach (GtkGridConstraint *self,
GtkConstraintSolver *solver,
GPtrArray *refs)
{
g_return_if_fail (self->refs == NULL);
self->solver = solver;
self->refs = g_ptr_array_ref (refs);
}
void gtk_grid_constraint_detach (GtkGridConstraint *self)
{
int i;
if (self->refs == NULL)
return;
for (i = 0; i < self->refs->len; i++)
{
GtkConstraintRef *ref = g_ptr_array_index (self->refs, i);
gtk_constraint_solver_remove_constraint (self->solver, ref);
}
g_clear_pointer (&self->refs, g_ptr_array_unref);
}

49
gtk/gtkgridconstraint.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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/>.
*/
#ifndef __GTK_GRID_CONSTRAINT_H__
#define __GTK_GRID_CONSTRAINT_H__
#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_GRID_CONSTRAINT (gtk_grid_constraint_get_type ())
/**
* GtkGridConstraint:
*
* An object used for managing constraints for children in
* a constraints layout that are to be arranged in a grid.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkGridConstraint, gtk_grid_constraint, GTK, GRID_CONSTRAINT, GObject)
GDK_AVAILABLE_IN_ALL
GtkGridConstraint * gtk_grid_constraint_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_grid_constraint_add (GtkGridConstraint *self,
GtkWidget *child,
int left,
int right,
int top,
int bottom);
G_END_DECLS
#endif /* __GTK_GRID_CONSTRAINT_H__ */

View File

@@ -0,0 +1,47 @@
/*
* 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 "gtkgridconstraint.h"
#include "gtkconstraintsolverprivate.h"
typedef struct {
GtkWidget *child;
int left;
int right;
int top;
int bottom;
} GtkGridConstraintChild;
struct _GtkGridConstraint {
GObject parent;
gboolean row_homogeneous;
gboolean column_homogeneous;
GPtrArray *children;
GtkConstraintSolver *solver;
GPtrArray *refs;
};
gboolean gtk_grid_constraint_is_attached (GtkGridConstraint *constraint);
void gtk_grid_constraint_attach (GtkGridConstraint *constraint,
GtkConstraintSolver *solver,
GPtrArray *refs);
void gtk_grid_constraint_detach (GtkGridConstraint *constraint);

View File

@@ -252,6 +252,7 @@ gtk_public_sources = files([
'gtkgesturezoom.c',
'gtkglarea.c',
'gtkgrid.c',
'gtkgridconstraint.c',
'gtkgridlayout.c',
'gtkheaderbar.c',
'gtkicontheme.c',