Compare commits

...

39 Commits

Author SHA1 Message Date
Matthias Clasen
0f23273c11 constraint guide: Avoid redundant constraints
There is no point in creating a stay for nat
size when min == max. And no point in a constraint
for <= G_MAXINT either.
2019-06-29 22:14:29 +00:00
Matthias Clasen
4599981200 constraint guide: Fix initial property values
This was overlooked when the max-width/max-height/
strength properties were added.
2019-06-29 22:13:41 +00:00
Matthias Clasen
f10550dbb5 grid constraint: avoid variables
Instead of creating variables for every row and column,
reuse existing child variables as far as possible.
2019-06-29 20:47:54 +00:00
Matthias Clasen
d1f034eab6 Remove unused code 2019-06-29 20:47:54 +00:00
Matthias Clasen
6301c436ff constraint solver: Use GTK_NOTE
Allow using GTK_DEBUG=constraints to get debug
output from the constraints solver.
2019-06-29 20:47:54 +00:00
Matthias Clasen
1ecff62083 solver: Add private statistics api
No point in keeping these counters without
a way to show them.
2019-06-29 19:14:33 +00:00
Matthias Clasen
055772e487 constraint layout: Dont reset constraints in allocate
If trust our allocation algorithm, this can never
trigger (and in fact, it never does).
2019-06-29 17:36:47 +00:00
Matthias Clasen
6f41adbe51 constraints: Add some internal apis
Checking if a set is empty or a singleton can
be done more efficiently than determining its size.
2019-06-29 14:23:57 +00:00
Matthias Clasen
ab01713234 constraints demo: Set guide strength
This makes us prefer to give the space its natural width.
2019-06-29 14:19:41 +00:00
Matthias Clasen
0221c9443c constraint guide: Make strength tweakable
The strength for the natural width can be used
as a tie-breaker to make instable systems behave
in a more predictable way. This can be seen
in the simple constraints demo in gtk-demo.
2019-06-29 14:19:16 +00:00
Matthias Clasen
895ffd5b7e constraints demo: Update comments 2019-06-29 10:15:42 -04:00
Matthias Clasen
db2ebb17ff constraint layout: freeze/thaw on mass ops
In measure and allocate we are potentially
changing quite a few constraints. Don't
optimize at every step.
2019-06-29 05:06:56 +00:00
Matthias Clasen
194ec5c3eb constraints: Use better data structures
Use a GSequence for GtkVariableSet, to avoid
quadratic behavior.
2019-06-29 04:59:49 +00:00
Matthias Clasen
267227aa75 constraint layout: go back to keeping constraints
We want to minimize changes to the solver, so
keep the size constraints around and only update
them when the size changes.
2019-06-29 04:17:46 +00:00
Matthias Clasen
0695869e57 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-29 00:31:56 +00:00
Matthias Clasen
d3f42e9705 Add a constraint grid demo
This shows a cross made from overlapping grids.
2019-06-29 00:23:32 +00:00
Matthias Clasen
bedd2d77b6 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-29 00:01:01 +00:00
Matthias Clasen
0f2f5db449 constraint layout: Measure min/nat size separately
Only constraint the opposite direction if we
actually have a for_size, and measure natural
size after removing the edit constraints. With
these changes, the test that compares constraint
layout to grid layout passes.
2019-06-28 23:03:23 +00:00
Matthias Clasen
783ce4c870 constraint layout: Use stays for natural size
It makes more sense to treat the natural size
of both children and guides as stays, since
we want to meet these values as closely as we
can, under the circumstances.
2019-06-28 23:03:23 +00:00
Matthias Clasen
761e2ae5e0 constraint layout: Add debug output for guides
Print out the allocation we end up giving to
guides. This helps in making sense of the
allocations of the child widgets that these
guides relate to.
2019-06-28 23:03:23 +00:00
Matthias Clasen
013c18040a constraints demo: Give space a name
Lets call it 'space'.
2019-06-28 23:03:23 +00:00
Matthias Clasen
3e0d6bdd9a constraint guide: Add a name property
We need to be able to print meaningful debug messages
regarding these objects, and eventually present them
in the inspector too.
2019-06-28 23:03:23 +00:00
Matthias Clasen
129030a0dc constraint guide: Shorten names
Now that this is its own source file,
no need for these prefixes anymore.
2019-06-28 23:03:23 +00:00
Matthias Clasen
695b4a6cae constraint layout: Measure more correctly
Set up all constraints for minimum + natural
width + height when measuring, regardless
of the orientation we're measuring. Anything
else will lead to incorrect answers when
there are constraints that cut across
dimensions.
2019-06-28 23:03:23 +00:00
Matthias Clasen
7bfb369fa3 solver: Fix a copy/paste error 2019-06-28 23:03:23 +00:00
Matthias Clasen
85fbd2c3b7 Add a constraint layout test
This test produces a very simple layout and
checks that it produces the same numbers as
an equivalent grid layout. Currently, it fails,
since the constraint layout can't produce
minimum sizes that are different from natural
sizes.
2019-06-28 23:03:22 +00:00
Matthias Clasen
7e11df9154 constrainttree: Fix criticals
We don't want the edit constraints to be required,
since we want to violate them when the pointer moves
off the edge. Make them strong instead, and now
everything just works.
2019-06-28 17:10:44 +00:00
Matthias Clasen
aed7d32cde constrainttree: Make each drag a single edit
Repeat suggestions work now, so we can implement
this as it is intended to, and have a single
edit phase for the entire drag operation.
2019-06-28 17:09:38 +00:00
Matthias Clasen
49897bdce8 constraint solver: Fix repeat suggestions
We were not storing the previous value, causing
the first two suggestions to work, but not later
ones.

Fixes the test added in the previous commit.
2019-06-28 17:02:40 +00:00
Matthias Clasen
ad087634fd Amend a constraint solver test
Make the 'repeat edit' test make more than to
suggestions in a single edit phase. It turns out
that this does not work, whereas just doing
two in a row does.
2019-06-28 16:30:07 +00:00
Emmanuele Bassi
7a190da5f4 Do not try to pivot after an optimization error
Bail out of the optimization, instead.
2019-06-28 14:34:48 +01:00
Emmanuele Bassi
b236cab3ba Remove stray variable unref() in ConstraintSolver
The marker variables table does not acquire a reference on the
constraint variables it contains, as they are part of the constraint
used as the key, and will be released when the constraint is removed.
2019-06-28 14:22:53 +01:00
Matthias Clasen
1e46874eb0 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 13:00:19 +00:00
Matthias Clasen
043fed8612 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 13:00:19 +00:00
Matthias Clasen
b0824a5784 demo: Use constraint guide api
Use proper api to create and set up
the guide, and also try max-width.
2019-06-28 13:00:19 +00:00
Matthias Clasen
469da877d7 Flesh out GtkConstraintGuide
This commit moves GtkConstraintGuide into its own
source files to avoid gtkconstraintlayout.c turning
too messy, adds max size properties and implements
getters and setters.
2019-06-28 12:59:12 +00:00
Matthias Clasen
6ecaa45fb8 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-28 12:48:15 +00:00
Matthias Clasen
7beca2a33a Detach guides on unroot
We don't want to leave constraints behind.
2019-06-28 12:48:07 +00:00
Matthias Clasen
d5f77b10b9 Simplify the guide implementation
Store the values and constraints in
arrays, to facilitate treating them
uniformly.
2019-06-28 12:47:53 +00:00
28 changed files with 4259 additions and 726 deletions

View File

@@ -44,20 +44,21 @@ simple_grid_class_init (SimpleGridClass *klass)
/* Layout:
*
* +-----------------------------+
* | +-----------+ +-----------+ |
* | | Child 1 | | Child 2 | |
* | +-----------+ +-----------+ |
* | +-------------------------+ |
* | | Child 3 | |
* | +-------------------------+ |
* +-----------------------------+
* +-------------------------------------+
* | +-----------++-------++-----------+ |
* | | Child 1 || Space || Child 2 | |
* | +-----------++-------++-----------+ |
* | +---------------------------------+ |
* | | Child 3 | |
* | +---------------------------------+ |
* +-------------------------------------+
*
* Constraints:
*
* super.start = child1.start - 8
* child1.width = child2.width
* child1.end = child2.start - 12
* child1.end = space.start
* space.end = child2.start
* child2.end = super.end - 8
* super.start = child3.start - 8
* child3.end = super.end - 8
@@ -69,6 +70,12 @@ simple_grid_class_init (SimpleGridClass *klass)
* child3.height = child2.height
* child3.bottom = super.bottom - 8
*
* To add some flexibility, we make the space
* stretchable:
*
* space.width >= 10
* space.width = 100
* space.width <= 200
*/
static void
build_constraints (SimpleGrid *self,
@@ -76,12 +83,12 @@ build_constraints (SimpleGrid *self,
{
GtkConstraintGuide *guide;
guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE,
"min-width", 10,
"min-height", 10,
"nat-width", 100,
"nat-height", 10,
NULL);
guide = gtk_constraint_guide_new ();
gtk_constraint_guide_set_name (guide, "space");
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_guide_set_strength (guide, GTK_CONSTRAINT_STRENGTH_STRONG);
gtk_constraint_layout_add_guide (manager, guide);
gtk_constraint_layout_add_constraint (manager,

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_constraints3 (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 ("/constraints4/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_constraints4 (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="/constraints4">
<file>words</file>
</gresource>
<gresource prefix="/css_accordion">
<file>css_accordion.css</file>
<file>reset.css</file>
@@ -152,6 +155,8 @@
<file>combobox.c</file>
<file>constraints.c</file>
<file>constraints2.c</file>
<file>constraints3.c</file>
<file>constraints4.c</file>
<file>css_accordion.c</file>
<file>css_basics.c</file>
<file>css_blendmodes.c</file>

View File

@@ -10,6 +10,8 @@ demos = files([
'combobox.c',
'constraints.c',
'constraints2.c',
'constraints3.c',
'constraints4.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

@@ -384,11 +384,8 @@ gtk_constraint_variable_is_dummy (const GtkConstraintVariable *variable)
* A set of variables.
*/
struct _GtkConstraintVariableSet {
/* HashSet<Variable>, owns a reference */
GHashTable *set;
/* List<Variable>, used for iterating */
GList *ordered_set;
/* List<Variable>, owns a reference */
GSequence *set;
/* Age of the set, to guard against mutations while iterating */
gint64 age;
@@ -405,8 +402,7 @@ gtk_constraint_variable_set_free (GtkConstraintVariableSet *set)
{
g_return_if_fail (set != NULL);
g_list_free (set->ordered_set);
g_hash_table_unref (set->set);
g_sequence_free (set->set);
g_free (set);
}
@@ -423,10 +419,7 @@ gtk_constraint_variable_set_new (void)
{
GtkConstraintVariableSet *res = g_new (GtkConstraintVariableSet, 1);
res->set = g_hash_table_new_full (NULL, NULL,
(GDestroyNotify) gtk_constraint_variable_unref,
NULL);
res->ordered_set = NULL;
res->set = g_sequence_new ((GDestroyNotify) gtk_constraint_variable_unref);
res->age = 0;
@@ -435,7 +428,8 @@ gtk_constraint_variable_set_new (void)
static int
sort_by_variable_id (gconstpointer a,
gconstpointer b)
gconstpointer b,
gpointer data)
{
const GtkConstraintVariable *va = a, *vb = b;
@@ -463,16 +457,17 @@ gboolean
gtk_constraint_variable_set_add (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable)
{
if (g_hash_table_contains (set->set, variable))
return FALSE;
GSequenceIter *iter;
g_hash_table_add (set->set, gtk_constraint_variable_ref (variable));
iter = g_sequence_search (set->set, variable, sort_by_variable_id, NULL);
if (!g_sequence_iter_is_end (iter))
{
GtkConstraintVariable *v = g_sequence_get (iter);
if (v->_id == variable->_id)
return FALSE;
}
/* This is a tricky bit; the variables in the set must be ordered
* not by insertion, but by the incremental id of each variable,
* as that's the expected iteration order
*/
set->ordered_set = g_list_insert_sorted (set->ordered_set, variable, sort_by_variable_id);
g_sequence_insert_before (iter, gtk_constraint_variable_ref (variable));
set->age += 1;
@@ -495,10 +490,12 @@ gboolean
gtk_constraint_variable_set_remove (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable)
{
if (g_hash_table_contains (set->set, variable))
GSequenceIter *iter;
iter = g_sequence_lookup (set->set, variable, sort_by_variable_id, NULL);
if (iter != NULL)
{
set->ordered_set = g_list_remove (set->ordered_set, variable);
g_hash_table_remove (set->set, variable);
g_sequence_remove (iter);
set->age += 1;
return TRUE;
@@ -518,7 +515,19 @@ gtk_constraint_variable_set_remove (GtkConstraintVariableSet *set,
int
gtk_constraint_variable_set_size (GtkConstraintVariableSet *set)
{
return g_hash_table_size (set->set);
return g_sequence_get_length (set->set);
}
gboolean
gtk_constraint_variable_set_is_empty (GtkConstraintVariableSet *set)
{
return g_sequence_is_empty (set->set);
}
gboolean
gtk_constraint_variable_set_is_singleton (GtkConstraintVariableSet *set)
{
return g_sequence_iter_next (g_sequence_get_begin_iter (set->set)) == g_sequence_get_end_iter (set->set);
}
/*< private >
@@ -529,7 +538,7 @@ gtk_constraint_variable_set_size (GtkConstraintVariableSet *set)
/* Keep in sync with GtkConstraintVariableSetIter */
typedef struct {
GtkConstraintVariableSet *set;
GList *current;
GSequenceIter *iter;
gint64 age;
} RealVariableSetIter;
@@ -552,7 +561,7 @@ gtk_constraint_variable_set_iter_init (GtkConstraintVariableSetIter *iter,
g_return_if_fail (set != NULL);
riter->set = set;
riter->current = NULL;
riter->iter = g_sequence_get_begin_iter (set->set);
riter->age = set->age;
}
@@ -576,15 +585,13 @@ gtk_constraint_variable_set_iter_next (GtkConstraintVariableSetIter *iter,
g_assert (riter->age == riter->set->age);
if (riter->current == NULL)
riter->current = riter->set->ordered_set;
else
riter->current = riter->current->next;
if (g_sequence_iter_is_end (riter->iter))
return FALSE;
if (riter->current != NULL)
*variable_p = riter->current->data;
*variable_p = g_sequence_get (riter->iter);
riter->iter = g_sequence_iter_next (riter->iter);
return riter->current != NULL;
return TRUE;
}
/*< private >

View File

@@ -97,6 +97,12 @@ gboolean
gtk_constraint_variable_set_remove (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_set_is_empty (GtkConstraintVariableSet *set);
gboolean
gtk_constraint_variable_set_is_singleton (GtkConstraintVariableSet *set);
int
gtk_constraint_variable_set_size (GtkConstraintVariableSet *set);

607
gtk/gtkconstraintguide.c Normal file
View File

@@ -0,0 +1,607 @@
/* 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 {
MIN_WIDTH,
MIN_HEIGHT,
NAT_WIDTH,
NAT_HEIGHT,
MAX_WIDTH,
MAX_HEIGHT,
LAST_VALUE
} GuideValue;
struct _GtkConstraintGuide
{
GObject parent_instance;
char *name;
int strength;
int values[LAST_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_VALUE];
};
struct _GtkConstraintGuideClass {
GObjectClass parent_class;
};
enum {
PROP_MIN_WIDTH = 1,
PROP_MIN_HEIGHT,
PROP_NAT_WIDTH,
PROP_NAT_HEIGHT,
PROP_MAX_WIDTH,
PROP_MAX_HEIGHT,
PROP_STRENGTH,
PROP_NAME,
LAST_PROP
};
static GParamSpec *guide_props[LAST_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->strength = GTK_CONSTRAINT_STRENGTH_MEDIUM;
guide->values[MIN_WIDTH] = 0;
guide->values[MIN_HEIGHT] = 0;
guide->values[NAT_WIDTH] = 0;
guide->values[NAT_HEIGHT] = 0;
guide->values[MAX_WIDTH] = G_MAXINT;
guide->values[MAX_HEIGHT] = G_MAXINT;
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;
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]);
guide->constraints[index] = NULL;
}
if (index == MIN_WIDTH || index == NAT_WIDTH || index == MAX_WIDTH)
var = gtk_constraint_layout_get_attribute (guide->layout, GTK_CONSTRAINT_ATTRIBUTE_WIDTH, "guide", NULL, guide->bound_attributes);
else
var = gtk_constraint_layout_get_attribute (guide->layout, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT, "guide", NULL, guide->bound_attributes);
/* We always install min-size constraints,
* but we avoid nat-size constraints if min == max
* and we avoid max-size constraints if max == G_MAXINT
*/
if (index == MIN_WIDTH || index == MIN_HEIGHT)
{
guide->constraints[index] =
gtk_constraint_solver_add_constraint (solver,
var,
GTK_CONSTRAINT_RELATION_GE,
gtk_constraint_expression_new (guide->values[index]),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
}
else if ((index == NAT_WIDTH && guide->values[MIN_WIDTH] != guide->values[MAX_WIDTH]) ||
(index == NAT_HEIGHT && guide->values[MIN_HEIGHT] != guide->values[MAX_HEIGHT]))
{
gtk_constraint_variable_set_value (var, guide->values[index]);
guide->constraints[index] =
gtk_constraint_solver_add_stay_variable (solver,
var,
guide->strength);
}
else if ((index == MAX_WIDTH || index == MAX_HEIGHT) &&
guide->values[index] < G_MAXINT)
{
guide->constraints[index] =
gtk_constraint_solver_add_constraint (solver,
var,
GTK_CONSTRAINT_RELATION_LE,
gtk_constraint_expression_new (guide->values[index]),
GTK_CONSTRAINT_WEIGHT_REQUIRED);
}
}
void
gtk_constraint_guide_update (GtkConstraintGuide *guide)
{
int i;
for (i = 0; i < LAST_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_VALUE; i++)
{
if (guide->constraints[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 PROP_MIN_WIDTH:
case PROP_MIN_HEIGHT:
case PROP_NAT_WIDTH:
case PROP_NAT_HEIGHT:
case PROP_MAX_WIDTH:
case 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);
if (index == MIN_WIDTH || index == MAX_WIDTH)
gtk_constraint_guide_update_constraint (self, NAT_WIDTH);
if (index == MIN_HEIGHT || index == MAX_HEIGHT)
gtk_constraint_guide_update_constraint (self, NAT_HEIGHT);
}
break;
case PROP_STRENGTH:
gtk_constraint_guide_set_strength (self, g_value_get_enum (value));
break;
case PROP_NAME:
gtk_constraint_guide_set_name (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_constraint_guide_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkConstraintGuide *self = GTK_CONSTRAINT_GUIDE (gobject);
switch (prop_id)
{
case PROP_MIN_WIDTH:
case PROP_MIN_HEIGHT:
case PROP_NAT_WIDTH:
case PROP_NAT_HEIGHT:
case PROP_MAX_WIDTH:
case PROP_MAX_HEIGHT:
g_value_set_int (value, self->values[prop_id - 1]);
break;
case PROP_STRENGTH:
g_value_set_int (value, self->strength);
break;
case PROP_NAME:
g_value_set_string (value, self->name);
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_free (self->name);
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[PROP_MIN_WIDTH] =
g_param_spec_int ("min-width",
"Minimum width",
"Minimum width",
0, G_MAXINT, 0,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[PROP_MIN_HEIGHT] =
g_param_spec_int ("min-height",
"Minimum height",
"Minimum height",
0, G_MAXINT, 0,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[PROP_NAT_WIDTH] =
g_param_spec_int ("nat-width",
"Natural width",
"Natural width",
0, G_MAXINT, 0,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[PROP_NAT_HEIGHT] =
g_param_spec_int ("nat-height",
"Natural height",
"Natural height",
0, G_MAXINT, 0,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[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[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);
guide_props[PROP_STRENGTH] =
g_param_spec_enum ("strength",
"Strength",
"The strength to use for natural size",
GTK_TYPE_CONSTRAINT_STRENGTH,
GTK_CONSTRAINT_STRENGTH_MEDIUM,
G_PARAM_READWRITE|
G_PARAM_EXPLICIT_NOTIFY);
guide_props[PROP_NAME] =
g_param_spec_string ("name",
"Name",
"A name to use in debug message",
NULL,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, LAST_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[MIN_WIDTH];
if (height)
*height = guide->values[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[NAT_WIDTH];
if (height)
*height = guide->values[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[MAX_WIDTH];
if (height)
*height = guide->values[MAX_HEIGHT];
}
const char *
gtk_constraint_guide_get_name (GtkConstraintGuide *guide)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide), NULL);
return guide->name;
}
void
gtk_constraint_guide_set_name (GtkConstraintGuide *guide,
const char *name)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
g_free (guide->name);
guide->name = g_strdup (name);
g_object_notify_by_pspec (G_OBJECT (guide), guide_props[PROP_NAME]);
}
GtkConstraintStrength
gtk_constraint_guide_get_strength (GtkConstraintGuide *guide)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide),
GTK_CONSTRAINT_STRENGTH_MEDIUM);
return guide->strength;
}
void
gtk_constraint_guide_set_strength (GtkConstraintGuide *guide,
GtkConstraintStrength strength)
{
g_return_if_fail (GTK_IS_CONSTRAINT_GUIDE (guide));
if (guide->strength == strength)
return;
guide->strength = strength;
g_object_notify_by_pspec (G_OBJECT (guide), guide_props[PROP_STRENGTH]);
gtk_constraint_guide_update_constraint (guide, NAT_WIDTH);
gtk_constraint_guide_update_constraint (guide, NAT_HEIGHT);
}

84
gtk/gtkconstraintguide.h Normal file
View File

@@ -0,0 +1,84 @@
/* 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>
#include <gtk/gtktypebuiltins.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);
GDK_AVAILABLE_IN_ALL
GtkConstraintStrength gtk_constraint_guide_get_strength (GtkConstraintGuide *guide);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_set_strength (GtkConstraintGuide *guide,
GtkConstraintStrength strength);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_guide_set_name (GtkConstraintGuide *guide,
const char *name);
GDK_AVAILABLE_IN_ALL
const char * gtk_constraint_guide_get_name (GtkConstraintGuide *guide);
G_END_DECLS

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

File diff suppressed because it is too large Load Diff

View File

@@ -20,12 +20,13 @@
#include <gtk/gtklayoutmanager.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkconstraintguide.h>
#include <gtk/gtkgridconstraint.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 ())
#define GTK_TYPE_CONSTRAINT_GUIDE (gtk_constraint_guide_get_type ())
/**
* GtkConstraintLayoutChild:
@@ -35,21 +36,6 @@ G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK, CONSTRAINT_LAYOUT_CHILD, GtkLayoutChild)
/**
* GtkConstraintGuide:
*
* An object that can be added to a #GtkConstraintLayout and be
* used in constraints like a widget, without being drawn. Guides
* have a minimal and natural size. Depending on the constraints
* that are applied, they can act like a guideline that widgets
* can be aligned to, or like 'flexible space'.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraintGuide, gtk_constraint_guide, GTK, CONSTRAINT_GUIDE, GObject)
GDK_AVAILABLE_IN_ALL
GtkConstraintGuide * gtk_constraint_guide_new (void);
/**
* GtkConstraintLayout:
*
@@ -69,6 +55,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);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_add_guide (GtkConstraintLayout *manager,
GtkConstraintGuide *guide);

View File

@@ -0,0 +1,37 @@
/*
* 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 "gtkconstraintlayout.h"
#include "gtkconstraintsolverprivate.h"
G_BEGIN_DECLS
GtkConstraintSolver *
gtk_constraint_layout_get_solver (GtkConstraintLayout *layout);
GtkConstraintVariable *
gtk_constraint_layout_get_attribute (GtkConstraintLayout *layout,
GtkConstraintAttribute attr,
const char *prefix,
GtkWidget *widget,
GHashTable *bound_attributes);
G_END_DECLS

View File

@@ -736,17 +736,12 @@ gtk_constraint_solver_optimize (GtkConstraintSolver *self,
self->optimize_count += 1;
#ifdef G_ENABLE_DEBUG
{
char *str;
str = gtk_constraint_variable_to_string (z);
g_debug ("optimize: %s\n", str);
g_free (str);
str = gtk_constraint_solver_to_string (self);
g_debug ("%s\n", str);
g_free (str);
}
if (GTK_DEBUG_CHECK (CONSTRAINTS))
{
char *str = gtk_constraint_variable_to_string (z);
g_message ("optimize: %s", str);
g_free (str);
}
#endif
while (TRUE)
@@ -802,28 +797,28 @@ gtk_constraint_solver_optimize (GtkConstraintSolver *self,
if (min_ratio == DBL_MAX)
{
g_debug ("Unbounded objective variable during optimization");
GTK_NOTE (CONSTRAINTS, g_message ("Unbounded objective variable during optimization"));
break;
}
#ifdef G_ENABLE_DEBUG
{
char *entry_s = gtk_constraint_variable_to_string (entry);
char *exit_s = gtk_constraint_variable_to_string (exit);
g_debug ("pivot(entry: %s, exit: %s)", entry_s, exit_s);
g_free (entry_s);
g_free (exit_s);
}
if (GTK_DEBUG_CHECK (CONSTRAINTS))
{
char *entry_s = gtk_constraint_variable_to_string (entry);
char *exit_s = gtk_constraint_variable_to_string (exit);
g_message ("pivot(entry: %s, exit: %s)", entry_s, exit_s);
g_free (entry_s);
g_free (exit_s);
}
#endif
gtk_constraint_solver_pivot (self, entry, exit);
}
#ifdef G_ENABLE_DEBUG
g_debug ("solver.optimize.time := %.3f ms (pass: %d)",
(float) (g_get_monotonic_time () - start_time) / 1000.f,
self->optimize_count);
#endif
GTK_NOTE (CONSTRAINTS,
g_message ("solver.optimize.time := %.3f ms (pass: %d)",
(float) (g_get_monotonic_time () - start_time) / 1000.f,
self->optimize_count));
}
/*< private >
@@ -944,8 +939,6 @@ gtk_constraint_solver_new_expression (GtkConstraintSolver *self,
gtk_constraint_expression_set_variable (expr, dummy_var, 1.0);
g_hash_table_insert (self->marker_vars, constraint, dummy_var);
gtk_constraint_variable_unref (dummy_var);
}
else
{
@@ -1056,15 +1049,17 @@ gtk_constraint_solver_dual_optimize (GtkConstraintSolver *self)
}
if (ratio == DBL_MAX)
g_critical ("INTERNAL: ratio == DBL_MAX in dual_optimize");
{
g_critical ("INTERNAL: ratio == DBL_MAX in dual_optimize");
break;
}
gtk_constraint_solver_pivot (self, entry_var, exit_var);
}
#ifdef G_ENABLE_DEBUG
g_debug ("dual_optimize.time := %.3f ms",
(float) (g_get_monotonic_time () - start_time) / 1000.f);
#endif
GTK_NOTE (CONSTRAINTS,
g_message ("dual_optimize.time := %.3f ms",
(float) (g_get_monotonic_time () - start_time) / 1000.f));
}
static void
@@ -1168,7 +1163,7 @@ gtk_constraint_solver_choose_subject (GtkConstraintSolver *self,
GtkConstraintVariableSet *cset = g_hash_table_lookup (self->columns, t_v);
if (cset == NULL ||
(gtk_constraint_variable_set_size (cset) == 1 &&
(gtk_constraint_variable_set_is_singleton (cset) &&
g_hash_table_contains (self->columns, self->objective)))
{
subject = t_v;
@@ -1205,7 +1200,8 @@ gtk_constraint_solver_choose_subject (GtkConstraintSolver *self,
if (!G_APPROX_VALUE (gtk_constraint_expression_get_constant (expression), 0.0, 0.001))
{
g_debug ("Unable to satisfy required constraint (choose_subject)");
GTK_NOTE (CONSTRAINTS,
g_message ("Unable to satisfy required constraint (choose_subject)"));
return NULL;
}
@@ -1266,14 +1262,17 @@ gtk_constraint_solver_add_with_artificial_variable (GtkConstraintSolver *self,
az_tableau_row = g_hash_table_lookup (self->rows, az);
if (!G_APPROX_VALUE (gtk_constraint_expression_get_constant (az_tableau_row), 0.0, 0.001))
{
char *str = gtk_constraint_expression_to_string (expression);
gtk_constraint_solver_remove_column (self, av);
gtk_constraint_solver_remove_row (self, az, TRUE);
g_debug ("Unable to satisfy a required constraint (add): %s", str);
g_free (str);
#ifdef G_ENABLE_DEBUG
if (GTK_DEBUG_CHECK (CONSTRAINTS))
{
char *str = gtk_constraint_expression_to_string (expression);
g_message ("Unable to satisfy a required constraint (add): %s", str);
g_free (str);
}
#endif
return;
}
@@ -1318,15 +1317,14 @@ gtk_constraint_solver_add_constraint_internal (GtkConstraintSolver *self,
&prev_constant);
#ifdef G_ENABLE_DEBUG
{
char *expr_s = gtk_constraint_expression_to_string (expr);
char *ref_s = gtk_constraint_ref_to_string (constraint);
g_debug ("Adding constraint '%s' (normalized expression: '%s')\n", ref_s, expr_s);
g_free (ref_s);
g_free (expr_s);
}
if (GTK_DEBUG_CHECK (CONSTRAINTS))
{
char *expr_s = gtk_constraint_expression_to_string (expr);
char *ref_s = gtk_constraint_ref_to_string (constraint);
g_message ("Adding constraint '%s' (normalized expression: '%s')", ref_s, expr_s);
g_free (ref_s);
g_free (expr_s);
}
#endif
if (constraint->is_stay)
@@ -1482,6 +1480,8 @@ gtk_constraint_solver_create_variable (GtkConstraintSolver *self,
{
GtkConstraintVariable *res;
self->var_counter++;
res = gtk_constraint_variable_new (name);
gtk_constraint_variable_set_prefix (res, prefix);
gtk_constraint_variable_set_value (res, value);
@@ -1511,10 +1511,9 @@ gtk_constraint_solver_resolve (GtkConstraintSolver *solver)
gtk_constraint_solver_reset_stay_constants (solver);
#ifdef G_ENABLE_DEBUG
g_debug ("resolve.time := %.3f ms",
(float) (g_get_monotonic_time () - start_time) / 1000.f);
#endif
GTK_NOTE (CONSTRAINTS,
g_message ("resolve.time := %.3f ms",
(float) (g_get_monotonic_time () - start_time) / 1000.f));
solver->needs_solving = FALSE;
}
@@ -1633,11 +1632,12 @@ gtk_constraint_solver_add_stay_variable (GtkConstraintSolver *self,
self);
#ifdef G_ENABLE_DEBUG
{
char *str = gtk_constraint_expression_to_string (res->expression);
g_debug ("Adding stay variable: %s", str);
g_free (str);
}
if (GTK_DEBUG_CHECK (CONSTRAINTS))
{
char *str = gtk_constraint_expression_to_string (res->expression);
g_message ("Adding stay variable: %s", str);
g_free (str);
}
#endif
gtk_constraint_solver_add_constraint_internal (self, res);
@@ -1735,7 +1735,7 @@ gtk_constraint_solver_remove_edit_variable (GtkConstraintSolver *self,
{
char *str = gtk_constraint_variable_to_string (variable);
g_critical ("Unknown stay variable '%s'", str);
g_critical ("Unknown edit variable '%s'", str);
g_free (str);
@@ -1866,7 +1866,7 @@ gtk_constraint_solver_remove_constraint (GtkConstraintSolver *self,
if (exit_var == NULL)
{
if (gtk_constraint_variable_set_size (set) == 0)
if (gtk_constraint_variable_set_is_empty (set))
gtk_constraint_solver_remove_column (self, marker);
else
{
@@ -1972,6 +1972,7 @@ gtk_constraint_solver_suggest_value (GtkConstraintSolver *self,
double value)
{
EditInfo *ei = g_hash_table_lookup (self->edit_var_map, variable);
double delta;
if (ei == NULL)
{
g_critical ("Suggesting value '%g' but variable %p is not editable",
@@ -1987,9 +1988,10 @@ gtk_constraint_solver_suggest_value (GtkConstraintSolver *self,
return;
}
ei->prev_constant = value - ei->prev_constant;
delta = value - ei->prev_constant;
ei->prev_constant = value;
gtk_constraint_solver_delta_edit_constant (self, ei->prev_constant, ei->eplus, ei->eminus);
gtk_constraint_solver_delta_edit_constant (self, delta, ei->eplus, ei->eminus);
}
/*< private >
@@ -2206,3 +2208,39 @@ gtk_constraint_solver_to_string (GtkConstraintSolver *solver)
return g_string_free (buf, FALSE);
}
char *
gtk_constraint_solver_statistics (GtkConstraintSolver *solver)
{
GString *buf = g_string_new (NULL);
g_string_append_printf (buf, "Variables: %d\n", solver->var_counter);
g_string_append_printf (buf, "Slack vars: %d\n", solver->slack_counter);
g_string_append_printf (buf, "Artificial vars: %d\n", solver->artificial_counter);
g_string_append_printf (buf, "Dummy vars: %d\n", solver->dummy_counter);
g_string_append_printf (buf, "Stay vars: %d\n", g_hash_table_size (solver->stay_var_map));
g_string_append_printf (buf, "Optimize count: %d\n", solver->optimize_count);
g_string_append_printf (buf, "Rows: %d\n", g_hash_table_size (solver->rows));
g_string_append_printf (buf, "Columns: %d\n", g_hash_table_size (solver->columns));
if (g_hash_table_size (solver->columns) > 0)
{
GHashTableIter iter;
gpointer val;
double sum = 0;
g_hash_table_iter_init (&iter, solver->columns);
while (g_hash_table_iter_next (&iter, NULL, &val))
{
GtkConstraintVariableSet *set = val;
sum += gtk_constraint_variable_set_size (set);
}
g_string_append_printf (buf, "Avg column size: %g\n", sum / g_hash_table_size (solver->columns));
}
g_string_append_printf (buf, "Infeasible rows: %d\n", solver->infeasible_rows->len);
g_string_append_printf (buf, "External basic variables: %d\n", g_hash_table_size (solver->external_rows));
g_string_append_printf (buf, "External parametric variables: %d\n", g_hash_table_size (solver->external_parametric_vars));
return g_string_free (buf, FALSE);
}

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
@@ -142,4 +162,7 @@ gtk_constraint_solver_clear (GtkConstraintSolver *solver);
char *
gtk_constraint_solver_to_string (GtkConstraintSolver *solver);
char *
gtk_constraint_solver_statistics (GtkConstraintSolver *solver);
G_END_DECLS

View File

@@ -51,7 +51,8 @@ typedef enum {
GTK_DEBUG_ACTIONS = 1 << 13,
GTK_DEBUG_RESIZE = 1 << 14,
GTK_DEBUG_LAYOUT = 1 << 15,
GTK_DEBUG_SNAPSHOT = 1 << 16
GTK_DEBUG_SNAPSHOT = 1 << 16,
GTK_DEBUG_CONSTRAINTS = 1 << 17,
} GtkDebugFlag;
#ifdef G_ENABLE_DEBUG

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

@@ -179,7 +179,8 @@ static const GDebugKey gtk_debug_keys[] = {
{ "actions", GTK_DEBUG_ACTIONS },
{ "resize", GTK_DEBUG_RESIZE },
{ "layout", GTK_DEBUG_LAYOUT },
{ "snapshot", GTK_DEBUG_SNAPSHOT }
{ "snapshot", GTK_DEBUG_SNAPSHOT },
{ "constraints", GTK_DEBUG_CONSTRAINTS },
};
#endif /* G_ENABLE_DEBUG */

View File

@@ -202,6 +202,7 @@ gtk_public_sources = files([
'gtkcombobox.c',
'gtkcomboboxtext.c',
'gtkcomposetable.c',
'gtkconstraintguide.c',
'gtkconstraintlayout.c',
'gtkconstraint.c',
'gtkcontainer.c',
@@ -252,6 +253,7 @@ gtk_public_sources = files([
'gtkgesturezoom.c',
'gtkglarea.c',
'gtkgrid.c',
'gtkgridconstraint.c',
'gtkgridlayout.c',
'gtkheaderbar.c',
'gtkicontheme.c',

382
tests/constrainttree.c Normal file
View File

@@ -0,0 +1,382 @@
#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)));
gtk_constraint_solver_add_edit_variable (solver,
drag_node->x_var,
GTK_CONSTRAINT_WEIGHT_STRONG);
gtk_constraint_solver_add_edit_variable (solver,
drag_node->y_var,
GTK_CONSTRAINT_WEIGHT_STRONG);
gtk_constraint_solver_begin_edit (solver);
}
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_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_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;
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);
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;
}

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

View File

@@ -0,0 +1,326 @@
/* Copyright (C) 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 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/>.
*/
#include <gtk/gtk.h>
#define GTK_TYPE_GIZMO (gtk_gizmo_get_type ())
#define GTK_GIZMO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_GIZMO, GtkGizmo))
#define GTK_GIZMO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_GIZMO, GtkGizmoClass))
#define GTK_IS_GIZMO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_GIZMO))
#define GTK_IS_GIZMO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_GIZMO))
#define GTK_GIZMO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_GIZMO, GtkGizmoClass))
typedef struct _GtkGizmo GtkGizmo;
struct _GtkGizmo {
GtkWidget parent;
int min_width;
int min_height;
int nat_width;
int nat_height;
int width;
int height;
};
typedef GtkWidgetClass GtkGizmoClass;
G_DEFINE_TYPE (GtkGizmo, gtk_gizmo, GTK_TYPE_WIDGET);
static void
gtk_gizmo_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkGizmo *self = GTK_GIZMO (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
*minimum = self->min_width;
*natural = self->nat_width;
}
else
{
*minimum = self->min_height;
*natural = self->nat_height;
}
}
static void
gtk_gizmo_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkGizmo *self = GTK_GIZMO (widget);
self->width = width;
self->height = height;
}
static void
gtk_gizmo_class_init (GtkGizmoClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->measure = gtk_gizmo_measure;
widget_class->size_allocate = gtk_gizmo_size_allocate;
}
static void
gtk_gizmo_init (GtkGizmo *self)
{
}
/* Create a layout with three children
*
* +--------+--------+
* | child1 | child2 |
* +--------+--------+
* | child3 |
* +-----------------+
*
* Verify that
* - the layout has the expected min and nat sizes
* - the children get their >=nat width when the layout does
* - test that allocating the layout larger keeps
* child1 and child2 at the same size
*/
static void
test_simple_layout (void)
{
GtkWidget *window;
GtkWidget *parent;
GtkLayoutManager *layout;
GtkConstraintLayout *manager;
GtkGizmo *child1;
GtkGizmo *child2;
GtkGizmo *child3;
int minimum, natural;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
parent = g_object_new (GTK_TYPE_GIZMO, NULL);
gtk_widget_set_name (parent, "parent");
gtk_container_add (GTK_CONTAINER (window), parent);
layout = gtk_constraint_layout_new ();
gtk_widget_set_layout_manager (parent, layout);
manager = GTK_CONSTRAINT_LAYOUT (layout);
child1 = g_object_new (GTK_TYPE_GIZMO, NULL);
child2 = g_object_new (GTK_TYPE_GIZMO, NULL);
child3 = g_object_new (GTK_TYPE_GIZMO, NULL);
gtk_widget_set_name (GTK_WIDGET (child1), "child1");
child1->min_width = 10;
child1->min_height = 10;
child1->nat_width = 50;
child1->nat_height = 50;
gtk_widget_set_name (GTK_WIDGET (child2), "child2");
child2->min_width = 20;
child2->min_height = 20;
child2->nat_width = 50;
child2->nat_height = 50;
gtk_widget_set_name (GTK_WIDGET (child3), "child3");
child3->min_width = 50;
child3->min_height = 10;
child3->nat_width = 50;
child3->nat_height = 50;
gtk_widget_set_parent (GTK_WIDGET (child1), parent);
gtk_widget_set_parent (GTK_WIDGET (child2), parent);
gtk_widget_set_parent (GTK_WIDGET (child3), parent);
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
child1,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child1,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_EQ,
child2,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child1,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_RELATION_EQ,
child2,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child1,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_RELATION_EQ,
child3,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child1,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
child2,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child2,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
child3,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child3,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_END,
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,
child1,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
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,
child2,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child1,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
child3,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child2,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
child3,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (child3,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
#if 0
gtk_widget_show (window);
g_timeout_add (1000, (GSourceFunc)gtk_main_quit, NULL);
gtk_main ();
#endif
gtk_layout_manager_measure (layout,
parent,
GTK_ORIENTATION_HORIZONTAL,
-1,
&minimum,
&natural,
NULL,
NULL);
g_assert_cmpint (minimum, ==, 50);
g_assert_cmpint (natural, ==, 100);
gtk_layout_manager_measure (layout,
parent,
GTK_ORIENTATION_VERTICAL,
-1,
&minimum,
&natural,
NULL,
NULL);
g_assert_cmpint (minimum, ==, 40);
g_assert_cmpint (natural, ==, 100);
gtk_layout_manager_allocate (layout, parent, 100, 100, 0);
g_assert_cmpint (child1->width, ==, 50);
g_assert_cmpint (child2->width, ==, 50);
g_assert_cmpint (child3->width, ==, 100);
g_assert_cmpint (child1->height, ==, 50);
g_assert_cmpint (child2->height, ==, 50);
g_assert_cmpint (child3->height, ==, 50);
gtk_widget_unparent (GTK_WIDGET (child1));
gtk_widget_unparent (GTK_WIDGET (child2));
gtk_widget_unparent (GTK_WIDGET (child3));
gtk_widget_destroy (parent);
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv);
g_test_add_func ("/constraint-layout/simple", test_simple_layout);
return g_test_run();
}

View File

@@ -254,15 +254,27 @@ constraint_solver_edit_var_suggest (void)
gtk_constraint_solver_suggest_value (solver, a, 2.0);
gtk_constraint_solver_resolve (solver);
g_test_message ("Check values after first edit");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 2.0, 0.001);
gtk_constraint_solver_suggest_value (solver, a, 10.0);
gtk_constraint_solver_resolve (solver);
g_test_message ("Check values after second edit");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 10.0, 0.001);
gtk_constraint_solver_suggest_value (solver, a, 12.0);
gtk_constraint_solver_resolve (solver);
g_test_message ("Check values after third edit");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 12.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 12.0, 0.001);
gtk_constraint_variable_unref (a);
gtk_constraint_variable_unref (b);

View File

@@ -17,6 +17,7 @@ tests = [
['builderparser'],
['cellarea'],
['check-icon-names'],
['constraint-layout'],
['constraint-solver', [
'../../gtk/gtkconstraintsolver.c',
'../../gtk/gtkconstraintexpression.c',