Compare commits

...

34 Commits

Author SHA1 Message Date
Matthias Clasen
0bd3173549 forgotten files 2019-06-30 23:02:26 +00:00
Matthias Clasen
7ddaef4cf2 Add a constraint editor demo
This is an initial cut at providing a tool
for interactive exploration of constraints.
2019-06-30 22:00:21 +00:00
Matthias Clasen
05488a59bd constraint layout: notice when guides change
We need to queue a resize here.
2019-06-30 21:50:07 +00:00
Matthias Clasen
0426431446 Fix a crash in action muxer destruction
I've seen a crash when the action muxer gets
disposed during widget destroy, and tries to
disconnect from widget signals too late.

There is no real need to disconnect, since the
only time an action muxer is going away is when
its widget is destroyed, so just don't do it.
2019-06-30 21:50:07 +00:00
Matthias Clasen
c4dbf27f15 Add gtk_constraint_layout_remove_all_constraints
Otherwise, there is no way of getting rid of
vfl constraints.
2019-06-29 23:00:57 +00:00
Matthias Clasen
30d5e94368 Mark the layout as changed
We should mark the layout as changed when
constraints are added or removed.
2019-06-29 23:00:51 +00:00
Emmanuele Bassi
ba5237e7c9 Add a C convenience function for VFL constraints
The dictionary-based function is convenient for language bindings, but C
developers will feel more at home with a variadic arguments list.
2019-06-29 19:04:29 +01:00
Emmanuele Bassi
2311532c7e Add Constraints/VFL demo
Shows how to describe constraints using VFL instead of constructing
objects manually.
2019-06-29 18:51:39 +01:00
Emmanuele Bassi
6be2a787e0 Allow adding constraints described through VFL 2019-06-29 18:50:43 +01:00
Emmanuele Bassi
1e725b66a5 Add VFL parser for constraints
Constraints can be expressed with a compact syntax, called VFL (visual
format language).
2019-06-29 18:32:33 +01:00
Emmanuele Bassi
f0933ba371 Remove GtkConstraintVariable.set_prefix()
The prefix and name are set at construction time.
2019-06-29 17:22:58 +01:00
Matthias Clasen
12d45c01b0 Add an interactive constraints demo 2019-06-28 13:11:22 +01:00
Matthias Clasen
fc3f4aafe5 Make the constraints demo more interesting
Add a max size to the buttons, to force the
space to open up.
2019-06-28 13:11:22 +01:00
Matthias Clasen
ffd8b19a87 Add GtkConstraintGuide
This is meant to be a flexible space.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
20c4a4bb21 Use generic pointers for constraint targets
Since GtkWidget implements GtkConstraintTarget, we can omit the explicit
cast, and validate the type at run time.
2019-06-28 13:11:22 +01:00
Matthias Clasen
f8647b02eb Redefine constraints with GtkConstraintTarget
This is in preparation for allowing non-widgets
to act as constraint targets.
2019-06-28 13:11:22 +01:00
Matthias Clasen
4b11e73be6 widget: Implement GtkConstraintTarget 2019-06-28 13:11:22 +01:00
Matthias Clasen
4de3e99fbe Add GtkConstraintTarget
This is an marker interface that we will
use to accept other things that widgets
in constraints.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
c81e04755d Notify a layout change when adding and removing constraints
Changing the set of constraints should cause a relayout.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
e4466dd4fe Fix the opposite size measurement in GtkConstraintLayout
We cannot use the given "for size" when querying our children, because
the constraint layout has no idea about the opposite size of its
children until the layout is complete.

Additionally, we should only suggest an opposite size for the layout if
we have one, instead of suggesting a weak zero size.
2019-06-28 13:11:22 +01:00
Emmanuele Bassi
19e7d412e3 Remove size constraints from ConstraintLayoutChild
The size constraints are transient to measurement and allocation, so
they don't really need to be stored inside the GtkLayoutChild subclass
created by a GtkConstraintLayout.
2019-06-27 17:38:02 +01:00
Matthias Clasen
80c27061c6 constraints: Make internal consistency required
The relations between left, right, width
and top, bottom, height are required for
internal consistency. It doesn't make sense
to ever drop these.

Changing the strength of these relations makes
my systems behave much more stable.
2019-06-27 17:38:02 +01:00
Matthias Clasen
bd5c558646 Add gtk_constraint_layout_remove_constraint
Otherwise, you can't do many interesting things.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
ad40630008 Do not release reference on the subject of an expression
We don't own the reference in the first place.
2019-06-27 17:38:02 +01:00
Matthias Clasen
f07ba4ac5b constraints solver: Avoid critials
When the solver is finalized with existing
constraints, we end up with criticals when
the constraints ref finalize code calls
back into the hash table. Avoid that by
emptying the hash table beforehand.
2019-06-27 17:38:02 +01:00
Matthias Clasen
391add73ec constraint solver: Fix thawing
There was an obviously wrong precondition here.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
a21562f270 Do not leak LayoutChild instances
Since the LayoutManager owns the LayoutChild it creates, it's also
responsible for mopping them up.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
124607d1e8 Add GtkConstraintLayout demo 2019-06-27 17:38:02 +01:00
Emmanuele Bassi
9756cb9482 Add GtkConstraintLayout
A layout manager using GtkConstraintSolver to measure and allocate
children.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
92f93a603b Propagate rooting and unrooting widgets to layout managers
Layout managers may need to get access to data attached to the root of a
scene graph.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
e7cd3d4633 window: Create a GtkConstraintSolver
Implement the GtkRoot getter for GtkConstraintSolver.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
5780ec4bf1 Assign a GtkConstraintSolver to each GtkRoot
Constraints need to work across different parents, so it's better to
have a single constraint solver per top level.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
ec14c1f605 Move the Root interface to a private header
We don't expect out of tree implementations of GtkRoot, and having the
interface structure private to the GTK code allows us to add virtual
functions involving private types.
2019-06-27 17:38:02 +01:00
Emmanuele Bassi
c09fada79e Add constraint solver
GtkConstraintSolver is an implementation of the Cassowary constraint
solving algorithm:

  http://constraints.cs.washington.edu/cassowary/

The Cassowary method allows to incrementally solve a tableau of linear
equations, in the form of:

  x = y × coefficient + constant

with different weights, or strengths, applied to each one.

These equations can be used to describe constraints applied to a layout
of UI elements, which allows layout managers using the Cassowary method
to quickly, and efficiently, lay out widgets in complex relations
between themselves and their parent container.
2019-06-27 17:38:02 +01:00
51 changed files with 12335 additions and 23 deletions

View File

@@ -0,0 +1,94 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#include "config.h"
#include "constraint-editor-application.h"
#include "constraint-editor-window.h"
struct _ConstraintEditorApplication
{
GtkApplication parent_instance;
};
G_DEFINE_TYPE(ConstraintEditorApplication, constraint_editor_application, GTK_TYPE_APPLICATION);
static void
constraint_editor_application_init (ConstraintEditorApplication *app)
{
}
static void
quit_activated (GSimpleAction *action,
GVariant *parameter,
gpointer data)
{
g_application_quit (G_APPLICATION (data));
}
static GActionEntry app_entries[] =
{
{ "quit", quit_activated, NULL, NULL, NULL }
};
static void
constraint_editor_application_startup (GApplication *app)
{
const char *quit_accels[2] = { "<Ctrl>Q", NULL };
GtkCssProvider *provider;
G_APPLICATION_CLASS (constraint_editor_application_parent_class)->startup (app);
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries),
app);
gtk_application_set_accels_for_action (GTK_APPLICATION (app), "app.quit", quit_accels);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider, "/org/gtk/gtk4/constraint-editor/constraint-editor.css");
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
static void
constraint_editor_application_activate (GApplication *app)
{
ConstraintEditorWindow *win;
win = constraint_editor_window_new (CONSTRAINT_EDITOR_APPLICATION (app));
gtk_window_present (GTK_WINDOW (win));
}
static void
constraint_editor_application_class_init (ConstraintEditorApplicationClass *class)
{
GApplicationClass *application_class = G_APPLICATION_CLASS (class);
application_class->startup = constraint_editor_application_startup;
application_class->activate = constraint_editor_application_activate;
}
ConstraintEditorApplication *
constraint_editor_application_new (void)
{
return g_object_new (CONSTRAINT_EDITOR_APPLICATION_TYPE,
"application-id", "org.gtk.gtk4.ConstraintEditor",
NULL);
}

View File

@@ -0,0 +1,28 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#pragma once
#include <gtk/gtk.h>
#define CONSTRAINT_EDITOR_APPLICATION_TYPE (constraint_editor_application_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintEditorApplication, constraint_editor_application, CONSTRAINT, EDITOR_APPLICATION, GtkApplication)
ConstraintEditorApplication *constraint_editor_application_new (void);

View File

@@ -0,0 +1,354 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#include "config.h"
#include "constraint-editor-window.h"
#include "constraint-view.h"
#include "constraint-editor.h"
#include "guide-editor.h"
struct _ConstraintEditorWindow
{
GtkApplicationWindow parent_instance;
GtkWidget *paned;
GtkWidget *view;
GtkWidget *list;
};
G_DEFINE_TYPE(ConstraintEditorWindow, constraint_editor_window, GTK_TYPE_APPLICATION_WINDOW);
gboolean
constraint_editor_window_load (ConstraintEditorWindow *self,
GFile *file)
{
GBytes *bytes;
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
if (bytes == NULL)
return FALSE;
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
{
g_bytes_unref (bytes);
return FALSE;
}
#if 0
gtk_text_buffer_get_end_iter (self->text_buffer, &end);
gtk_text_buffer_insert (self->text_buffer,
&end,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
#endif
g_bytes_unref (bytes);
return TRUE;
}
static void
constraint_editor_window_finalize (GObject *object)
{
//ConstraintEditorWindow *self = (ConstraintEditorWindow *)object;
G_OBJECT_CLASS (constraint_editor_window_parent_class)->finalize (object);
}
static int child_counter;
static int guide_counter;
static void
add_child (ConstraintEditorWindow *win)
{
char *name;
child_counter++;
name = g_strdup_printf ("Child %d", child_counter);
constraint_view_add_child (CONSTRAINT_VIEW (win->view), name);
g_free (name);
}
static void
add_guide (ConstraintEditorWindow *win)
{
char *name;
GtkConstraintGuide *guide;
guide_counter++;
name = g_strdup_printf ("Guide %d", guide_counter);
guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE, NULL);
g_object_set_data_full (G_OBJECT (guide), "name", name, g_free);
constraint_view_add_guide (CONSTRAINT_VIEW (win->view), guide);
}
static void
constraint_editor_done (ConstraintEditor *editor,
GtkConstraint *constraint,
ConstraintEditorWindow *win)
{
GtkConstraint *old_constraint;
g_object_get (editor, "constraint", &old_constraint, NULL);
if (old_constraint)
constraint_view_remove_constraint (CONSTRAINT_VIEW (win->view), old_constraint);
constraint_view_add_constraint (CONSTRAINT_VIEW (win->view), constraint);
g_clear_object (&old_constraint);
gtk_widget_destroy (gtk_widget_get_ancestor (GTK_WIDGET (editor), GTK_TYPE_WINDOW));
}
static void
edit_constraint (ConstraintEditorWindow *win,
GtkConstraint *constraint)
{
GtkWidget *window;
ConstraintEditor *editor;
GListModel *model;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (win));
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
if (constraint)
gtk_window_set_title (GTK_WINDOW (window), "Edit Constraint");
else
gtk_window_set_title (GTK_WINDOW (window), "Create Constraint");
model = constraint_view_get_model (CONSTRAINT_VIEW (win->view));
editor = constraint_editor_new (model, constraint);
gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (editor));
g_signal_connect (editor, "done", G_CALLBACK (constraint_editor_done), win);
gtk_widget_show (window);
}
static void
add_constraint (ConstraintEditorWindow *win)
{
edit_constraint (win, NULL);
}
static void
guide_editor_done (GuideEditor *editor,
GtkConstraintGuide *guide,
ConstraintEditorWindow *win)
{
constraint_view_guide_changed (CONSTRAINT_VIEW (win->view), guide);
gtk_widget_destroy (gtk_widget_get_ancestor (GTK_WIDGET (editor), GTK_TYPE_WINDOW));
}
static void
edit_guide (ConstraintEditorWindow *win,
GtkConstraintGuide *guide)
{
GtkWidget *window;
GuideEditor *editor;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (win));
gtk_window_set_title (GTK_WINDOW (window), "Edit Guide");
editor = guide_editor_new (guide);
gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (editor));
g_signal_connect (editor, "done", G_CALLBACK (guide_editor_done), win);
gtk_widget_show (window);
}
static void
row_activated (GtkListBox *list,
GtkListBoxRow *row,
ConstraintEditorWindow *win)
{
GObject *item;
item = G_OBJECT (g_object_get_data (G_OBJECT (row), "item"));
if (GTK_IS_CONSTRAINT (item))
edit_constraint (win, GTK_CONSTRAINT (item));
else if (GTK_IS_CONSTRAINT_GUIDE (item))
edit_guide (win, GTK_CONSTRAINT_GUIDE (item));
}
static void
constraint_editor_window_class_init (ConstraintEditorWindowClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
g_type_ensure (CONSTRAINT_VIEW_TYPE);
object_class->finalize = constraint_editor_window_finalize;
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gtk/gtk4/constraint-editor/constraint-editor-window.ui");
gtk_widget_class_bind_template_child (widget_class, ConstraintEditorWindow, paned);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditorWindow, view);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditorWindow, list);
gtk_widget_class_bind_template_callback (widget_class, add_child);
gtk_widget_class_bind_template_callback (widget_class, add_guide);
gtk_widget_class_bind_template_callback (widget_class, add_constraint);
gtk_widget_class_bind_template_callback (widget_class, row_activated);
}
static void
row_edit (GtkButton *button,
ConstraintEditorWindow *win)
{
GtkWidget *row;
GObject *item;
row = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_LIST_BOX_ROW);
item = (GObject *)g_object_get_data (G_OBJECT (row), "item");
if (GTK_IS_CONSTRAINT (item))
edit_constraint (win, GTK_CONSTRAINT (item));
else if (GTK_IS_CONSTRAINT_GUIDE (item))
edit_guide (win, GTK_CONSTRAINT_GUIDE (item));
}
static void
mark_constraints_invalid (ConstraintEditorWindow *win,
gpointer removed)
{
GtkWidget *child;
GObject *item;
for (child = gtk_widget_get_first_child (win->list);
child;
child = gtk_widget_get_next_sibling (child))
{
item = (GObject *)g_object_get_data (G_OBJECT (child), "item");
if (GTK_IS_CONSTRAINT (item))
{
GtkConstraint *constraint = GTK_CONSTRAINT (item);
if (gtk_constraint_get_target (constraint) == (GtkConstraintTarget *)removed ||
gtk_constraint_get_source (constraint) == (GtkConstraintTarget *)removed)
{
GtkWidget *button;
button = (GtkWidget *)g_object_get_data (G_OBJECT (child), "edit");
gtk_button_set_icon_name (GTK_BUTTON (button), "dialog-warning-symbolic");
gtk_widget_set_tooltip_text (button, "Constraint is invalid");
}
}
}
}
static void
row_delete (GtkButton *button,
ConstraintEditorWindow *win)
{
GtkWidget *row;
GObject *item;
row = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_LIST_BOX_ROW);
item = (GObject *)g_object_get_data (G_OBJECT (row), "item");
if (GTK_IS_CONSTRAINT (item))
constraint_view_remove_constraint (CONSTRAINT_VIEW (win->view),
GTK_CONSTRAINT (item));
else if (GTK_IS_CONSTRAINT_GUIDE (item))
{
mark_constraints_invalid (win, item);
constraint_view_remove_guide (CONSTRAINT_VIEW (win->view),
GTK_CONSTRAINT_GUIDE (item));
}
else if (GTK_IS_WIDGET (item))
{
mark_constraints_invalid (win, item);
constraint_view_remove_child (CONSTRAINT_VIEW (win->view),
GTK_WIDGET (item));
}
}
static GtkWidget *
create_widget_func (gpointer item,
gpointer user_data)
{
ConstraintEditorWindow *win = user_data;
const char *name;
GtkWidget *row, *box, *label, *button;
name = (const char *)g_object_get_data (G_OBJECT (item), "name");
row = gtk_list_box_row_new ();
g_object_set_data (G_OBJECT (row), "item", item);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
label = gtk_label_new (name);
g_object_set (label,
"margin", 10,
NULL);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_widget_set_hexpand (label, TRUE);
gtk_container_add (GTK_CONTAINER (row), box);
gtk_container_add (GTK_CONTAINER (box), label);
if (GTK_IS_CONSTRAINT (item) || GTK_IS_CONSTRAINT_GUIDE (item))
{
button = gtk_button_new_from_icon_name ("document-edit-symbolic");
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
g_signal_connect (button, "clicked", G_CALLBACK (row_edit), win);
g_object_set_data (G_OBJECT (row), "edit", button);
gtk_container_add (GTK_CONTAINER (box), button);
button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
g_signal_connect (button, "clicked", G_CALLBACK (row_delete), win);
gtk_container_add (GTK_CONTAINER (box), button);
}
else if (GTK_IS_WIDGET (item))
{
button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
g_signal_connect (button, "clicked", G_CALLBACK (row_delete), win);
gtk_container_add (GTK_CONTAINER (box), button);
}
return row;
}
static void
constraint_editor_window_init (ConstraintEditorWindow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
gtk_list_box_bind_model (GTK_LIST_BOX (self->list),
constraint_view_get_model (CONSTRAINT_VIEW (self->view)),
create_widget_func,
self,
NULL);
}
ConstraintEditorWindow *
constraint_editor_window_new (ConstraintEditorApplication *application)
{
return g_object_new (CONSTRAINT_EDITOR_WINDOW_TYPE,
"application", application,
NULL);
}

View File

@@ -0,0 +1,34 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#pragma once
#include <gtk/gtk.h>
#include "constraint-editor-application.h"
#define CONSTRAINT_EDITOR_WINDOW_TYPE (constraint_editor_window_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintEditorWindow, constraint_editor_window, CONSTRAINT, EDITOR_WINDOW, GtkApplicationWindow)
ConstraintEditorWindow * constraint_editor_window_new (ConstraintEditorApplication *application);
gboolean constraint_editor_window_load (ConstraintEditorWindow *self,
GFile *file);

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ConstraintEditorWindow" parent="GtkApplicationWindow">
<style>
<class name="devel"/>
</style>
<property name="title" translatable="yes">GTK Constraint Editor</property>
<property name="default-width">1024</property>
<property name="default-height">768</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="header">
<property name="title" translatable="yes">GTK Constraint Editor</property>
<property name="show-title-buttons">1</property>
</object>
</child>
<child>
<object class="GtkPaned" id="paned">
<property name="orientation">horizontal</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<child>
<object class="GtkButton">
<property name="label">Add Child</property>
<signal name="clicked" handler="add_child" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="label">Add Guide</property>
<signal name="clicked" handler="add_guide" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="label">Add Constraint</property>
<signal name="clicked" handler="add_constraint" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="vscrollbar-policy">automatic</property>
<property name="vexpand">1</property>
<child>
<object class="GtkListBox" id="list">
<property name="show-separators">1</property>
<property name="selection-mode">none</property>
<signal name="row-activated" handler="row_activated"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="ConstraintView" id="view">
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,583 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#include "config.h"
#include "constraint-editor.h"
struct _ConstraintEditor
{
GtkWidget parent_instance;
GtkWidget *grid;
GtkWidget *name;
GtkWidget *target;
GtkWidget *target_attr;
GtkWidget *relation;
GtkWidget *source;
GtkWidget *source_attr;
GtkWidget *multiplier;
GtkWidget *constant;
GtkWidget *strength;
GtkWidget *preview;
GtkWidget *button;
GtkConstraint *constraint;
GListModel *model;
gboolean constructed;
};
enum {
PROP_MODEL = 1,
PROP_CONSTRAINT,
LAST_PROP
};
static GParamSpec *pspecs[LAST_PROP];
enum {
DONE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE(ConstraintEditor, constraint_editor, GTK_TYPE_WIDGET);
static const char *
get_target_name (GtkConstraintTarget *target)
{
if (target == NULL)
return "super";
else
return (const char *)g_object_get_data (G_OBJECT (target), "name");
}
static void
constraint_target_combo (GListModel *model,
GtkWidget *combo,
gboolean is_source)
{
int i;
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "super", "Super");
if (model)
{
for (i = 0; i < g_list_model_get_n_items (model); i++)
{
GObject *item = g_list_model_get_object (model, i);
const char *name;
if (GTK_IS_CONSTRAINT (item))
continue;
name = get_target_name (GTK_CONSTRAINT_TARGET (item));
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), name, name);
g_object_unref (item);
}
}
}
static void
constraint_attribute_combo (GtkWidget *combo,
gboolean is_source)
{
if (is_source)
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "none", "None");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "left", "Left");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "right", "Right");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "top", "Top");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "bottom", "Bottom");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "start", "Start");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "end", "End");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "width", "Width");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "height", "Height");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "center-x", "Center X");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "center-y", "Center Y");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "baseline", "Baseline");
}
static void
constraint_relation_combo (GtkWidget *combo)
{
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "le", "");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "eq", "=");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "ge", "");
}
static void
constraint_strength_combo (GtkWidget *combo)
{
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "weak", "Weak");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "medium", "Medium");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "strong", "Strong");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "required", "Required");
}
static gpointer
get_target (GListModel *model,
const char *id)
{
int i;
if (strcmp ("super", id) == 0)
return NULL;
for (i = 0; i < g_list_model_get_n_items (model); i++)
{
GObject *item = g_list_model_get_object (model, i);
const char *name;
if (GTK_IS_CONSTRAINT (item))
continue;
name = (const char *)g_object_get_data (item, "name");
g_object_unref (item);
if (strcmp (name, id) == 0)
return item;
}
return NULL;
}
static GtkConstraintAttribute
get_target_attr (const char *id)
{
GtkConstraintAttribute attr;
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_ATTRIBUTE);
GEnumValue *value = g_enum_get_value_by_nick (class, id);
attr = value->value;
g_type_class_unref (class);
return attr;
}
static const char *
get_attr_nick (GtkConstraintAttribute attr)
{
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_ATTRIBUTE);
GEnumValue *value = g_enum_get_value (class, attr);
const char *nick = value->value_nick;
g_type_class_unref (class);
return nick;
}
static GtkConstraintRelation
get_relation (const char *id)
{
GtkConstraintRelation relation;
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_RELATION);
GEnumValue *value = g_enum_get_value_by_nick (class, id);
relation = value->value;
g_type_class_unref (class);
return relation;
}
static const char *
get_relation_nick (GtkConstraintRelation relation)
{
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_RELATION);
GEnumValue *value = g_enum_get_value (class, relation);
const char *nick = value->value_nick;
g_type_class_unref (class);
return nick;
}
static GtkConstraintStrength
get_strength (const char *id)
{
GtkConstraintStrength strength;
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_STRENGTH);
GEnumValue *value = g_enum_get_value_by_nick (class, id);
strength = value->value;
g_type_class_unref (class);
return strength;
}
static const char *
get_strength_nick (GtkConstraintStrength strength)
{
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_STRENGTH);
GEnumValue *value = g_enum_get_value (class, strength);
const char *nick = value->value_nick;
g_type_class_unref (class);
return nick;
}
static void
create_constraint (GtkButton *button,
ConstraintEditor *editor)
{
const char *id;
gpointer target;
GtkConstraintAttribute target_attr;
gpointer source;
GtkConstraintAttribute source_attr;
GtkConstraintRelation relation;
double multiplier;
double constant;
int strength;
GtkConstraint *constraint;
const char *name;
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target));
target = get_target (editor->model, id);
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target_attr));
target_attr = get_target_attr (id);
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source));
source = get_target (editor->model, id);
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
source_attr = get_target_attr (id);
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->relation));
relation = get_relation (id);
multiplier = g_ascii_strtod (gtk_editable_get_text (GTK_EDITABLE (editor->multiplier)), NULL);
constant = g_ascii_strtod (gtk_editable_get_text (GTK_EDITABLE (editor->constant)), NULL);
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->strength));
strength = get_strength (id);
name = gtk_editable_get_text (GTK_EDITABLE (editor->name));
constraint = gtk_constraint_new (target, target_attr,
relation,
source, source_attr,
multiplier,
constant,
strength);
g_object_set_data_full (G_OBJECT (constraint), "name", g_strdup (name), g_free);
g_signal_emit (editor, signals[DONE], 0, constraint);
g_object_unref (constraint);
}
static void
source_attr_changed (ConstraintEditor *editor)
{
const char *id;
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
if (strcmp (id, "none") == 0)
{
gtk_combo_box_set_active (GTK_COMBO_BOX (editor->source), -1);
gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), "");
gtk_widget_set_sensitive (editor->source, FALSE);
gtk_widget_set_sensitive (editor->multiplier, FALSE);
}
else
{
gtk_widget_set_sensitive (editor->source, TRUE);
gtk_widget_set_sensitive (editor->multiplier, TRUE);
}
}
static void
update_preview (ConstraintEditor *editor)
{
GString *str;
const char *name;
const char *attr;
char *relation;
const char *multiplier;
const char *constant;
double c, m;
if (!editor->constructed)
return;
str = g_string_new ("");
name = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target));
attr = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target_attr));
relation = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (editor->relation));
if (name == NULL)
name = "[ ]";
g_string_append_printf (str, "%s.%s %s ", name, attr, relation);
g_free (relation);
constant = gtk_editable_get_text (GTK_EDITABLE (editor->constant));
c = g_ascii_strtod (constant, NULL);
attr = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
if (strcmp (attr, "none") != 0)
{
name = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source));
multiplier = gtk_editable_get_text (GTK_EDITABLE (editor->multiplier));
m = g_ascii_strtod (multiplier, NULL);
if (name == NULL)
name = "[ ]";
g_string_append_printf (str, "%s.%s", name, attr);
if (m != 1.0)
g_string_append_printf (str, " × %g", m);
if (c > 0.0)
g_string_append_printf (str, " + %g", c);
else if (c < 0.0)
g_string_append_printf (str, " - %g", -c);
}
else
g_string_append_printf (str, "%g", c);
gtk_label_set_label (GTK_LABEL (editor->preview), str->str);
g_string_free (str, TRUE);
}
static void
update_button (ConstraintEditor *editor)
{
if (gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target)) != NULL &&
gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source)) != NULL)
gtk_widget_set_sensitive (editor->button, TRUE);
else
gtk_widget_set_sensitive (editor->button, FALSE);
}
static void
constraint_editor_init (ConstraintEditor *editor)
{
gtk_widget_init_template (GTK_WIDGET (editor));
}
static int constraint_counter;
static void
constraint_editor_constructed (GObject *object)
{
ConstraintEditor *editor = CONSTRAINT_EDITOR (object);
constraint_target_combo (editor->model, editor->target, FALSE);
constraint_attribute_combo (editor->target_attr, FALSE);
constraint_relation_combo (editor->relation);
constraint_target_combo (editor->model, editor->source, TRUE);
constraint_attribute_combo (editor->source_attr, TRUE);
constraint_strength_combo (editor->strength);
if (editor->constraint)
{
GtkConstraintTarget *target;
GtkConstraintAttribute attr;
GtkConstraintRelation relation;
GtkConstraintStrength strength;
const char *nick;
char *val;
double multiplier;
double constant;
nick = (char *)g_object_get_data (G_OBJECT (editor->constraint), "name");
gtk_editable_set_text (GTK_EDITABLE (editor->name), nick);
target = gtk_constraint_get_target (editor->constraint);
nick = get_target_name (target);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->target), nick);
attr = gtk_constraint_get_target_attribute (editor->constraint);
nick = get_attr_nick (attr);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->target_attr), nick);
target = gtk_constraint_get_source (editor->constraint);
nick = get_target_name (target);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->source), nick);
attr = gtk_constraint_get_source_attribute (editor->constraint);
nick = get_attr_nick (attr);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->source_attr), nick);
relation = gtk_constraint_get_relation (editor->constraint);
nick = get_relation_nick (relation);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->relation), nick);
multiplier = gtk_constraint_get_multiplier (editor->constraint);
val = g_strdup_printf ("%g", multiplier);
gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), val);
g_free (val);
constant = gtk_constraint_get_constant (editor->constraint);
val = g_strdup_printf ("%g", constant);
gtk_editable_set_text (GTK_EDITABLE (editor->constant), val);
g_free (val);
strength = gtk_constraint_get_strength (editor->constraint);
nick = get_strength_nick (strength);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), nick);
gtk_button_set_label (GTK_BUTTON (editor->button), "Apply");
}
else
{
char *name;
constraint_counter++;
name = g_strdup_printf ("Constraint %d", constraint_counter);
gtk_editable_set_text (GTK_EDITABLE (editor->name), name);
g_free (name);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->target_attr), "left");
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->source_attr), "left");
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->relation), "eq");
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), "required");
gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), "1.0");
gtk_editable_set_text (GTK_EDITABLE (editor->constant), "0.0");
gtk_button_set_label (GTK_BUTTON (editor->button), "Create");
}
editor->constructed = TRUE;
update_preview (editor);
update_button (editor);
}
static void
constraint_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
ConstraintEditor *self = CONSTRAINT_EDITOR (object);
switch (property_id)
{
case PROP_MODEL:
self->model = g_value_dup_object (value);
break;
case PROP_CONSTRAINT:
self->constraint = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
constraint_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
ConstraintEditor *self = CONSTRAINT_EDITOR (object);
switch (property_id)
{
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
case PROP_CONSTRAINT:
g_value_set_object (value, self->constraint);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
constraint_editor_dispose (GObject *object)
{
ConstraintEditor *self = (ConstraintEditor *)object;
g_clear_pointer (&self->grid, gtk_widget_unparent);
g_clear_object (&self->model);
g_clear_object (&self->constraint);
G_OBJECT_CLASS (constraint_editor_parent_class)->dispose (object);
}
static void
constraint_editor_class_init (ConstraintEditorClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->constructed = constraint_editor_constructed;
object_class->dispose = constraint_editor_dispose;
object_class->set_property = constraint_editor_set_property;
object_class->get_property = constraint_editor_get_property;
pspecs[PROP_CONSTRAINT] =
g_param_spec_object ("constraint", "constraint", "constraint",
GTK_TYPE_CONSTRAINT,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
pspecs[PROP_MODEL] =
g_param_spec_object ("model", "model", "model",
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, LAST_PROP, pspecs);
signals[DONE] =
g_signal_new ("done",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 1, GTK_TYPE_CONSTRAINT);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gtk/gtk4/constraint-editor/constraint-editor.ui");
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, grid);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, name);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, target);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, target_attr);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, relation);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, source);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, source_attr);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, multiplier);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, constant);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, strength);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, preview);
gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, button);
gtk_widget_class_bind_template_callback (widget_class, update_preview);
gtk_widget_class_bind_template_callback (widget_class, update_button);
gtk_widget_class_bind_template_callback (widget_class, create_constraint);
gtk_widget_class_bind_template_callback (widget_class, source_attr_changed);
}
ConstraintEditor *
constraint_editor_new (GListModel *model,
GtkConstraint *constraint)
{
return g_object_new (CONSTRAINT_EDITOR_TYPE,
"model", model,
"constraint", constraint,
NULL);
}

View File

@@ -0,0 +1,12 @@
constraintview {
background: black;
color: white;
}
constraintview .child {
background: red;
}
constraintview .guide {
background: blue;
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gtk/gtk4/constraint-editor">
<file preprocess="xml-stripblanks">constraint-editor-window.ui</file>
<file preprocess="xml-stripblanks">constraint-editor.ui</file>
<file preprocess="xml-stripblanks">guide-editor.ui</file>
<file>constraint-editor.css</file>
</gresource>
</gresources>

View File

@@ -0,0 +1,29 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#pragma once
#include <gtk/gtk.h>
#define CONSTRAINT_EDITOR_TYPE (constraint_editor_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintEditor, constraint_editor, CONSTRAINT, EDITOR, GtkWidget)
ConstraintEditor * constraint_editor_new (GListModel *model,
GtkConstraint *constraint);

View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ConstraintEditor" parent="GtkWidget">
<child>
<object class="GtkGrid" id="grid">
<property name="margin">20</property>
<property name="row-spacing">10</property>
<property name="column-spacing">10</property>
<child>
<object class="GtkLabel">
<property name="label">Name</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkEntry" id="name">
<layout>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Target</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="target">
<signal name="changed" handler="update_preview" swapped="yes"/>
<signal name="changed" handler="update_button" swapped="yes"/>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="target_attr">
<signal name="changed" handler="update_preview" swapped="yes"/>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Relation</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="relation">
<signal name="changed" handler="update_preview" swapped="yes"/>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Source</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="source">
<signal name="changed" handler="update_preview" swapped="yes"/>
<signal name="changed" handler="update_button" swapped="yes"/>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="source_attr">
<signal name="changed" handler="update_preview" swapped="yes"/>
<signal name="changed" handler="source_attr_changed" swapped="yes"/>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Multiplier</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</layout>
</object>
</child>
<child>
<object class="GtkEntry" id="multiplier">
<signal name="changed" handler="update_preview" swapped="yes"/>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Constant</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
</layout>
</object>
</child>
<child>
<object class="GtkEntry" id="constant">
<signal name="changed" handler="update_preview" swapped="yes"/>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Strength</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="strength">
<layout>
<property name="left-attach">1</property>
<property name="top-attach">6</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="preview">
<property name="xalign">0</property>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">7</property>
<property name="column-span">2</property>
</layout>
<attributes>
<attribute name="scale" value="1.44"/>
</attributes>
</object>
</child>
<child>
<object class="GtkButton" id="button">
<property name="label">Create</property>
<signal name="clicked" handler="create_constraint"/>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">8</property>
</layout>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,93 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#include "constraint-view-child.h"
struct _ConstraintViewChild
{
GObject parent_instance;
char *name;
};
enum {
PROP_NAME = 1,
LAST_PROP
};
static GParamSpec props[LAST_PROP];
G_DEFINE_TYPE (ConstraintViewChild, constraint_view_child, G_TYPE_OBJECT)
static void
constraint_view_child_init (ConstraintViewChild *child)
{
}
static void
constraint_view_child_finalize (GObject *object)
{
ConstraintViewChild *child = CONSTRAINT_VIEW_CHILD (object);
g_free (child->name);
G_OBJECT_CLASS (constraint_view_child_parent_class)->finalize (object);
}
static void
constraint_view_child_set_property (GObject *object,
static void
constraint_view_child_class_init (ConstraintViewChildClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = constraint_view_child_finalize;
object_class->get_property = constraint_view_child_get_property;
object_class->set_property = constraint_view_child_set_property;
props[PROP_NAME] =
g_param_spec_string ("name", "name", "name",
NULL,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, LAST_PROP, props);
}
#define CONSTRAINT_VIEW_CHILD_TYPE (constraint_view_get_type ())
G_DECLARE_TYPE (ConstraintViewChild, constraint_view_child, CONSTRAINT, VIEW_CHILD, GObject)
#define CONSTRAINT_VIEW_WIDGET_TYPE (constraint_view_widget_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintViewWidget, constraint_view_widget, CONSTRAINT, VIEW_WIDGET, ConstraintViewChild)
ConstraintViewWidget * constraint_view_widget_new (void);
#define CONSTRAINT_VIEW_GUIDE_TYPE (constraint_view_guide_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintViewGuide, constraint_view_guide, CONSTRAINT, VIEW_GUIDE, ConstraintViewChild)
ConstraintViewGuide * constraint_view_guide_new (void);
#define CONSTRAINT_VIEW_CONSTRAINT_TYPE (constraint_view_constraint_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintViewConstraint, constraint_view_constraint, CONSTRAINT, VIEW_CONSTRAINT, ConstraintViewChild)
ConstraintViewGuide * constraint_view_constraint_new (void);

View File

@@ -0,0 +1,44 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#pragma once
#include <gtk/gtk.h>
#define CONSTRAINT_VIEW_CHILD_TYPE (constraint_view_get_type ())
G_DECLARE_TYPE (ConstraintViewChild, constraint_view_child, CONSTRAINT, VIEW_CHILD, GObject)
#define CONSTRAINT_VIEW_WIDGET_TYPE (constraint_view_widget_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintViewWidget, constraint_view_widget, CONSTRAINT, VIEW_WIDGET, ConstraintViewChild)
ConstraintViewWidget * constraint_view_widget_new (void);
#define CONSTRAINT_VIEW_GUIDE_TYPE (constraint_view_guide_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintViewGuide, constraint_view_guide, CONSTRAINT, VIEW_GUIDE, ConstraintViewChild)
ConstraintViewGuide * constraint_view_guide_new (void);
#define CONSTRAINT_VIEW_CONSTRAINT_TYPE (constraint_view_constraint_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintViewConstraint, constraint_view_constraint, CONSTRAINT, VIEW_CONSTRAINT, ConstraintViewChild)
ConstraintViewGuide * constraint_view_constraint_new (void);

View File

@@ -0,0 +1,277 @@
/* 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>
#include "constraint-view.h"
struct _ConstraintView
{
GtkWidget parent;
GListStore *store;
};
G_DEFINE_TYPE (ConstraintView, constraint_view, GTK_TYPE_WIDGET);
static void
constraint_view_dispose (GObject *object)
{
ConstraintView *view = CONSTRAINT_VIEW (object);
GtkWidget *child;
while ((child = gtk_widget_get_first_child (GTK_WIDGET (view))) != NULL)
gtk_widget_unparent (child);
g_clear_object (&view->store);
G_OBJECT_CLASS (constraint_view_parent_class)->dispose (object);
}
static void
constraint_view_class_init (ConstraintViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = constraint_view_dispose;
gtk_widget_class_set_css_name (widget_class, "constraintview");
}
static void
constraint_view_init (ConstraintView *self)
{
gtk_widget_set_layout_manager (GTK_WIDGET (self),
gtk_constraint_layout_new ());
self->store = g_list_store_new (G_TYPE_OBJECT);
}
ConstraintView *
constraint_view_new (void)
{
return g_object_new (CONSTRAINT_VIEW_TYPE, NULL);
}
void
constraint_view_add_child (ConstraintView *view,
const char *name)
{
GtkWidget *frame;
GtkWidget *label;
label = gtk_label_new (name);
frame = gtk_frame_new (NULL);
gtk_style_context_add_class (gtk_widget_get_style_context (frame), "child");
g_object_set_data_full (G_OBJECT (frame), "name", g_strdup (name), g_free);
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_widget_set_parent (frame, GTK_WIDGET (view));
g_list_store_append (view->store, frame);
}
void
constraint_view_remove_child (ConstraintView *view,
GtkWidget *child)
{
int i;
gtk_widget_unparent (child);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (view->store)); i++)
{
if (g_list_model_get_item (G_LIST_MODEL (view->store), i) == (GObject*)child)
{
g_list_store_remove (view->store, i);
break;
}
}
}
void
constraint_view_add_guide (ConstraintView *view,
GtkConstraintGuide *guide)
{
GtkLayoutManager *manager;
GtkWidget *frame;
GtkWidget *label;
const char *name;
GtkConstraint *constraint;
name = (const char *)g_object_get_data (G_OBJECT (guide), "name");
label = gtk_label_new (name);
frame = gtk_frame_new (NULL);
gtk_style_context_add_class (gtk_widget_get_style_context (frame), "guide");
g_object_set_data_full (G_OBJECT (frame), "name", g_strdup (name), g_free);
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_widget_set_parent (frame, GTK_WIDGET (view));
g_object_set_data (G_OBJECT (guide), "frame", frame);
g_object_set_data (G_OBJECT (guide), "label", label);
manager = gtk_widget_get_layout_manager (GTK_WIDGET (view));
gtk_constraint_layout_add_guide (GTK_CONSTRAINT_LAYOUT (manager),
g_object_ref (guide));
constraint = gtk_constraint_new (frame,
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
GTK_CONSTRAINT_RELATION_EQ,
guide,
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
g_object_set_data (G_OBJECT (guide), "left-constraint", constraint);
constraint = gtk_constraint_new (frame,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
guide,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
g_object_set_data (G_OBJECT (guide), "top-constraint", constraint);
constraint = gtk_constraint_new (frame,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_EQ,
guide,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
g_object_set_data (G_OBJECT (guide), "width-constraint", constraint);
constraint = gtk_constraint_new (frame,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_RELATION_EQ,
guide,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
1.0, 0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
g_object_set_data (G_OBJECT (guide), "height-constraint", constraint);
g_list_store_append (view->store, guide);
}
void
constraint_view_guide_changed (ConstraintView *view,
GtkConstraintGuide *guide)
{
GtkWidget *label;
const char *name;
int i;
name = (const char *)g_object_get_data (G_OBJECT (guide), "name");
label = (GtkWidget *)g_object_get_data (G_OBJECT (guide), "label");
gtk_label_set_label (GTK_LABEL (label), name);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (view->store)); i++)
{
if (g_list_model_get_item (G_LIST_MODEL (view->store), i) == (GObject*)guide)
{
g_list_model_items_changed (G_LIST_MODEL (view->store), i, 1, 1);
break;
}
}
}
void
constraint_view_remove_guide (ConstraintView *view,
GtkConstraintGuide *guide)
{
GtkLayoutManager *manager;
GtkWidget *frame;
GtkConstraint *constraint;
int i;
manager = gtk_widget_get_layout_manager (GTK_WIDGET (view));
constraint = (GtkConstraint*)g_object_get_data (G_OBJECT (guide), "left-constraint");
gtk_constraint_layout_remove_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
constraint = (GtkConstraint*)g_object_get_data (G_OBJECT (guide), "top-constraint");
gtk_constraint_layout_remove_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
constraint = (GtkConstraint*)g_object_get_data (G_OBJECT (guide), "width-constraint");
gtk_constraint_layout_remove_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
constraint = (GtkConstraint*)g_object_get_data (G_OBJECT (guide), "height-constraint");
gtk_constraint_layout_remove_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
frame = (GtkWidget *)g_object_get_data (G_OBJECT (guide), "frame");
gtk_widget_unparent (frame);
gtk_constraint_layout_remove_guide (GTK_CONSTRAINT_LAYOUT (manager),
guide);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (view->store)); i++)
{
if (g_list_model_get_item (G_LIST_MODEL (view->store), i) == (GObject*)guide)
{
g_list_store_remove (view->store, i);
break;
}
}
}
void
constraint_view_add_constraint (ConstraintView *view,
GtkConstraint *constraint)
{
GtkLayoutManager *manager;
manager = gtk_widget_get_layout_manager (GTK_WIDGET (view));
gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager),
g_object_ref (constraint));
g_list_store_append (view->store, constraint);
}
void
constraint_view_remove_constraint (ConstraintView *view,
GtkConstraint *constraint)
{
GtkLayoutManager *manager;
int i;
manager = gtk_widget_get_layout_manager (GTK_WIDGET (view));
gtk_constraint_layout_remove_constraint (GTK_CONSTRAINT_LAYOUT (manager),
constraint);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (view->store)); i++)
{
if (g_list_model_get_item (G_LIST_MODEL (view->store), i) == (GObject*)constraint)
{
g_list_store_remove (view->store, i);
break;
}
}
}
GListModel *
constraint_view_get_model (ConstraintView *view)
{
return G_LIST_MODEL (view->store);
}

View File

@@ -0,0 +1,44 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#pragma once
#include <gtk/gtk.h>
#define CONSTRAINT_VIEW_TYPE (constraint_view_get_type ())
G_DECLARE_FINAL_TYPE (ConstraintView, constraint_view, CONSTRAINT, VIEW, GtkWidget)
ConstraintView * constraint_view_new (void);
void constraint_view_add_child (ConstraintView *view,
const char *name);
void constraint_view_remove_child (ConstraintView *view,
GtkWidget *child);
void constraint_view_add_guide (ConstraintView *view,
GtkConstraintGuide *guide);
void constraint_view_remove_guide (ConstraintView *view,
GtkConstraintGuide *guide);
void constraint_view_guide_changed (ConstraintView *view,
GtkConstraintGuide *guide);
void constraint_view_add_constraint (ConstraintView *view,
GtkConstraint *constraint);
void constraint_view_remove_constraint (ConstraintView *view,
GtkConstraint *constraint);
GListModel * constraint_view_get_model (ConstraintView *view);

View File

@@ -0,0 +1,316 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#include "config.h"
#include "guide-editor.h"
struct _GuideEditor
{
GtkWidget parent_instance;
GtkWidget *grid;
GtkWidget *name;
GtkWidget *min_width;
GtkWidget *min_height;
GtkWidget *nat_width;
GtkWidget *nat_height;
GtkWidget *max_width;
GtkWidget *max_height;
GtkWidget *strength;
GtkWidget *button;
GtkConstraintGuide *guide;
gboolean constructed;
};
enum {
PROP_GUIDE = 1,
LAST_PROP
};
static GParamSpec *pspecs[LAST_PROP];
enum {
DONE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE(GuideEditor, guide_editor, GTK_TYPE_WIDGET);
static void
guide_strength_combo (GtkWidget *combo)
{
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "weak", "Weak");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "medium", "Medium");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "strong", "Strong");
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), "required", "Required");
}
static GtkConstraintStrength
get_strength (const char *id)
{
GtkConstraintStrength strength;
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_STRENGTH);
GEnumValue *value = g_enum_get_value_by_nick (class, id);
strength = value->value;
g_type_class_unref (class);
return strength;
}
const char *
get_strength_nick (GtkConstraintStrength strength)
{
GEnumClass *class = g_type_class_ref (GTK_TYPE_CONSTRAINT_STRENGTH);
GEnumValue *value = g_enum_get_value (class, strength);
const char *nick = value->value_nick;
g_type_class_unref (class);
return nick;
}
static void
create_guide (GtkButton *button,
GuideEditor *editor)
{
#if 0
const char *id;
int strength;
#endif
const char *name;
int w, h;
GtkConstraintGuide *guide;
//guide = gtk_constraint_guide_new ();
if (editor->guide)
guide = g_object_ref (editor->guide);
else
guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE, NULL);
name = gtk_editable_get_text (GTK_EDITABLE (editor->name));
g_object_set_data_full (G_OBJECT (guide), "name", g_strdup (name), g_free);
w = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->min_width));
h = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->min_height));
//gtk_constraint_guide_set_min_size (guide, w, h);
g_object_set (guide, "min-width", w, "min-height", h, NULL);
w = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->nat_width));
h = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->nat_height));
//gtk_constraint_guide_set_nat_size (guide, w, h);
g_object_set (guide, "nat-width", w, "nat-height", h, NULL);
#if 0
w = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->max_width));
h = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->max_height));
gtk_constraint_guide_set_max_size (guide, w, h);
id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->strength));
strength = get_strength (id);
gtk_constraint_guide_set_strength (guide, strength);
#endif
g_signal_emit (editor, signals[DONE], 0, guide);
g_object_unref (guide);
}
static void
guide_editor_init (GuideEditor *editor)
{
gtk_widget_init_template (GTK_WIDGET (editor));
}
static int guide_counter;
static void
guide_editor_constructed (GObject *object)
{
GuideEditor *editor = GUIDE_EDITOR (object);
guide_strength_combo (editor->strength);
gtk_widget_set_sensitive (editor->max_width, FALSE);
gtk_widget_set_sensitive (editor->max_height, FALSE);
gtk_widget_set_sensitive (editor->strength, FALSE);
if (editor->guide)
{
#if 0
GtkConstaintStrength strength;
#endif
const char *nick;
int w, h;
nick = (char *)g_object_get_data (G_OBJECT (editor->guide), "name");
gtk_editable_set_text (GTK_EDITABLE (editor->name), nick);
//gtk_constaint_guide_get_min_size (editor->guide, &w, &h);
g_object_get (editor->guide, "min-width", &w, "min-height", &h, NULL);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_width), w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_height), h);
//gtk_constaint_guide_get_nat_size (editor->guide, &w, &h);
g_object_get (editor->guide, "nat-width", &w, "nat-height", &h, NULL);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_width), w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_height), h);
#if 0
gtk_constaint_guide_get_max_size (editor->guide, &w, &h);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_width), w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_height), h);
strength = gtk_guide_get_strength (editor->guide);
nick = get_strength_nick (strength);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), nick);
#endif
gtk_button_set_label (GTK_BUTTON (editor->button), "Apply");
}
else
{
char *name;
guide_counter++;
name = g_strdup_printf ("Guide %d", guide_counter);
gtk_editable_set_text (GTK_EDITABLE (editor->name), name);
g_free (name);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_width), 0.0);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_height), 0.0);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_width), 0.0);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_height), 0.0);
#if 0
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_width), G_MAXINT);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_height), G_MAXINT);
gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), "medium");
#endif
gtk_button_set_label (GTK_BUTTON (editor->button), "Create");
}
editor->constructed = TRUE;
}
static void
guide_editor_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GuideEditor *self = GUIDE_EDITOR (object);
switch (property_id)
{
case PROP_GUIDE:
self->guide = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
guide_editor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GuideEditor *self = GUIDE_EDITOR (object);
switch (property_id)
{
case PROP_GUIDE:
g_value_set_object (value, self->guide);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
guide_editor_dispose (GObject *object)
{
GuideEditor *self = (GuideEditor *)object;
g_clear_pointer (&self->grid, gtk_widget_unparent);
g_clear_object (&self->guide);
G_OBJECT_CLASS (guide_editor_parent_class)->dispose (object);
}
static void
guide_editor_class_init (GuideEditorClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->constructed = guide_editor_constructed;
object_class->dispose = guide_editor_dispose;
object_class->set_property = guide_editor_set_property;
object_class->get_property = guide_editor_get_property;
pspecs[PROP_GUIDE] =
g_param_spec_object ("guide", "guide", "guide",
GTK_TYPE_CONSTRAINT_GUIDE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, LAST_PROP, pspecs);
signals[DONE] =
g_signal_new ("done",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 1, GTK_TYPE_CONSTRAINT_GUIDE);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gtk/gtk4/constraint-editor/guide-editor.ui");
gtk_widget_class_bind_template_child (widget_class, GuideEditor, grid);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, name);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, min_width);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, min_height);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, nat_width);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, nat_height);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, max_width);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, max_height);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, strength);
gtk_widget_class_bind_template_child (widget_class, GuideEditor, button);
gtk_widget_class_bind_template_callback (widget_class, create_guide);
}
GuideEditor *
guide_editor_new (GtkConstraintGuide *guide)
{
return g_object_new (GUIDE_EDITOR_TYPE,
"guide", guide,
NULL);
}

View File

@@ -0,0 +1,28 @@
/*
* 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/>.
*
* Authors: Matthias Clasen
*/
#pragma once
#include <gtk/gtk.h>
#define GUIDE_EDITOR_TYPE (guide_editor_get_type ())
G_DECLARE_FINAL_TYPE (GuideEditor, guide_editor, GUIDE, EDITOR, GtkWidget)
GuideEditor * guide_editor_new (GtkConstraintGuide *guide);

View File

@@ -0,0 +1,188 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkAdjustment" id="min_width_adj">
<property name="lower">0</property>
<property name="upper">2147483647</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
<property name="page-size">0</property>
</object>
<object class="GtkAdjustment" id="min_height_adj">
<property name="lower">0</property>
<property name="upper">2147483647</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
<property name="page-size">0</property>
</object>
<object class="GtkAdjustment" id="nat_width_adj">
<property name="lower">0</property>
<property name="upper">2147483647</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
<property name="page-size">0</property>
</object>
<object class="GtkAdjustment" id="nat_height_adj">
<property name="lower">0</property>
<property name="upper">2147483647</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
<property name="page-size">0</property>
</object>
<object class="GtkAdjustment" id="max_width_adj">
<property name="lower">0</property>
<property name="upper">2147483647</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
<property name="page-size">0</property>
</object>
<object class="GtkAdjustment" id="max_height_adj">
<property name="lower">0</property>
<property name="upper">2147483647</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
<property name="page-size">0</property>
</object>
<template class="GuideEditor" parent="GtkWidget">
<child>
<object class="GtkGrid" id="grid">
<property name="margin">20</property>
<property name="row-spacing">10</property>
<property name="column-spacing">10</property>
<child>
<object class="GtkLabel">
<property name="label">Name</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkEntry" id="name">
<property name="max-width-chars">20</property>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Min Size</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkSpinButton" id="min_width">
<property name="adjustment">min_width_adj</property>
<property name="max-width-chars">5</property>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkSpinButton" id="min_height">
<property name="adjustment">min_height_adj</property>
<property name="max-width-chars">5</property>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Nat Size</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkSpinButton" id="nat_width">
<property name="adjustment">nat_width_adj</property>
<property name="max-width-chars">5</property>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkSpinButton" id="nat_height">
<property name="adjustment">nat_height_adj</property>
<property name="max-width-chars">5</property>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Max Size</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkSpinButton" id="max_width">
<property name="adjustment">max_width_adj</property>
<property name="max-width-chars">5</property>
<layout>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkSpinButton" id="max_height">
<property name="adjustment">max_height_adj</property>
<property name="max-width-chars">5</property>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Strength</property>
<layout>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</layout>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="strength">
<layout>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="button">
<property name="label">Create</property>
<signal name="clicked" handler="create_guide"/>
<layout>
<property name="left-attach">2</property>
<property name="top-attach">5</property>
</layout>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,28 @@
/*
* 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/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include <constraint-editor-application.h>
int
main (int argc, char *argv[])
{
return g_application_run (G_APPLICATION (constraint_editor_application_new ()), argc, argv);
}

View File

@@ -0,0 +1,19 @@
constraint_editor_sources = [
'main.c',
'constraint-editor-application.c',
'constraint-editor-window.c',
'constraint-view.c',
'constraint-editor.c',
'guide-editor.c',
]
constraint_editor_resources = gnome.compile_resources('constraint_editor_resources',
'constraint-editor.gresource.xml',
source_dir: '.')
executable('gtk4-constraint-editor',
constraint_editor_sources, constraint_editor_resources,
dependencies: libgtk_dep,
include_directories: confinc,
gui_app: true,
install: false)

View File

@@ -0,0 +1,282 @@
/* Constraints/Simple
*
* GtkConstraintLayout provides a layout manager that uses relations
* between widgets (also known as "constraints") to compute the position
* and size of each child.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE (SimpleGrid, simple_grid, SIMPLE, GRID, GtkWidget)
struct _SimpleGrid
{
GtkWidget parent_instance;
GtkWidget *button1, *button2;
GtkWidget *button3;
};
G_DEFINE_TYPE (SimpleGrid, simple_grid, GTK_TYPE_WIDGET)
static void
simple_grid_destroy (GtkWidget *widget)
{
SimpleGrid *self = SIMPLE_GRID (widget);
g_clear_pointer (&self->button1, gtk_widget_destroy);
g_clear_pointer (&self->button2, gtk_widget_destroy);
g_clear_pointer (&self->button3, gtk_widget_destroy);
GTK_WIDGET_CLASS (simple_grid_parent_class)->destroy (widget);
}
static void
simple_grid_class_init (SimpleGridClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->destroy = simple_grid_destroy;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
/* Layout:
*
* +-----------------------------+
* | +-----------+ +-----------+ |
* | | Child 1 | | Child 2 | |
* | +-----------+ +-----------+ |
* | +-------------------------+ |
* | | Child 3 | |
* | +-------------------------+ |
* +-----------------------------+
*
* Constraints:
*
* super.start = child1.start - 8
* child1.width = child2.width
* child1.end = child2.start - 12
* child2.end = super.end - 8
* super.start = child3.start - 8
* child3.end = super.end - 8
* super.top = child1.top - 8
* super.top = child2.top - 8
* child1.bottom = child3.top - 12
* child2.bottom = child3.top - 12
* child3.height = child1.height
* child3.height = child2.height
* child3.bottom = super.bottom - 8
*
*/
static void
build_constraints (SimpleGrid *self,
GtkConstraintLayout *manager)
{
GtkConstraintGuide *guide;
guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE,
"min-width", 10,
"min-height", 10,
"nat-width", 100,
"nat-height", 10,
NULL);
gtk_constraint_layout_add_guide (manager, guide);
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new_constant (GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_LE,
200.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
self->button1,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button1,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_EQ,
self->button2,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button1,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
guide,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (guide,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
self->button2,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button2,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
self->button3,
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button3,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
self->button1,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
self->button2,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button1,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
self->button3,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
-12.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button2,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
self->button3,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
-12.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button3,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_RELATION_EQ,
self->button1,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button3,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_RELATION_EQ,
self->button2,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (self->button3,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
}
static void
simple_grid_init (SimpleGrid *self)
{
GtkWidget *widget = 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");
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
build_constraints (self, GTK_CONSTRAINT_LAYOUT (manager));
}
GtkWidget *
do_constraints (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 (simple_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,245 @@
/* Constraints/Interactive
*
* Demonstrate how constraints can be updates during
* user interaction.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE (InteractiveGrid, interactive_grid, INTERACTIVE, GRID, GtkWidget)
struct _InteractiveGrid
{
GtkWidget parent_instance;
GtkWidget *button1, *button2;
GtkWidget *button3;
GtkConstraintGuide *guide;
GtkConstraint *constraint;
};
G_DEFINE_TYPE (InteractiveGrid, interactive_grid, GTK_TYPE_WIDGET)
static void
interactive_grid_destroy (GtkWidget *widget)
{
InteractiveGrid *self = INTERACTIVE_GRID (widget);
g_clear_pointer (&self->button1, gtk_widget_destroy);
g_clear_pointer (&self->button2, gtk_widget_destroy);
g_clear_pointer (&self->button3, gtk_widget_destroy);
GTK_WIDGET_CLASS (interactive_grid_parent_class)->destroy (widget);
}
static void
interactive_grid_class_init (InteractiveGridClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->destroy = interactive_grid_destroy;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
static void
build_constraints (InteractiveGrid *self,
GtkConstraintLayout *manager)
{
self->guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE, NULL);
gtk_constraint_layout_add_guide (manager, self->guide);
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new_constant (GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_RELATION_EQ,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_END,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_START,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (NULL,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_TOP,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button1),
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_RELATION_EQ,
GTK_CONSTRAINT_TARGET (self->button2),
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
gtk_constraint_layout_add_constraint (manager,
gtk_constraint_new (GTK_CONSTRAINT_TARGET (self->button3),
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_RELATION_EQ,
NULL,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
1.0,
-8.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED));
}
static void
drag_cb (GtkGestureDrag *drag,
double offset_x,
double offset_y,
InteractiveGrid *self)
{
GtkConstraintLayout *layout = GTK_CONSTRAINT_LAYOUT (gtk_widget_get_layout_manager (GTK_WIDGET (self)));
double x, y;
if (self->constraint)
{
gtk_constraint_layout_remove_constraint (layout, self->constraint);
g_clear_object (&self->constraint);
}
gtk_gesture_drag_get_start_point (drag, &x, &y);
self->constraint = gtk_constraint_new_constant (GTK_CONSTRAINT_TARGET (self->guide),
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
GTK_CONSTRAINT_RELATION_EQ,
x + offset_x,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
gtk_constraint_layout_add_constraint (layout, g_object_ref (self->constraint));
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
static void
interactive_grid_init (InteractiveGrid *self)
{
GtkWidget *widget = GTK_WIDGET (self);
GtkGesture *drag;
self->button1 = gtk_button_new_with_label ("Child 1");
gtk_widget_set_parent (self->button1, widget);
gtk_widget_set_name (self->button1, "button1");
self->button2 = gtk_button_new_with_label ("Child 2");
gtk_widget_set_parent (self->button2, widget);
gtk_widget_set_name (self->button2, "button2");
self->button3 = gtk_button_new_with_label ("Child 3");
gtk_widget_set_parent (self->button3, widget);
gtk_widget_set_name (self->button3, "button3");
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
build_constraints (self, GTK_CONSTRAINT_LAYOUT (manager));
drag = gtk_gesture_drag_new ();
g_signal_connect (drag, "drag-update", G_CALLBACK (drag_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag));
}
GtkWidget *
do_constraints2 (GtkWidget *do_widget)
{
static GtkWidget *window;
if (!window)
{
GtkWidget *header, *box, *grid, *button;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
header = gtk_header_bar_new ();
gtk_header_bar_set_title (GTK_HEADER_BAR (header), "Constraints");
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), FALSE);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_add (GTK_CONTAINER (window), box);
grid = g_object_new (interactive_grid_get_type (), NULL);
gtk_widget_set_hexpand (grid, TRUE);
gtk_widget_set_vexpand (grid, TRUE);
gtk_container_add (GTK_CONTAINER (box), grid);
button = gtk_button_new_with_label ("Close");
gtk_container_add (GTK_CONTAINER (box), button);
gtk_widget_set_hexpand (grid, TRUE);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (gtk_widget_destroy), window);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,165 @@
/* Constraints/VFL
*
* GtkConstraintLayout allows defining constraints using a
* compact syntax called Visual Format Language, or VFL.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE (VflGrid, vfl_grid, VFL, GRID, GtkWidget)
struct _VflGrid
{
GtkWidget parent_instance;
GtkWidget *button1, *button2;
GtkWidget *button3;
};
G_DEFINE_TYPE (VflGrid, vfl_grid, GTK_TYPE_WIDGET)
static void
vfl_grid_destroy (GtkWidget *widget)
{
VflGrid *self = VFL_GRID (widget);
g_clear_pointer (&self->button1, gtk_widget_destroy);
g_clear_pointer (&self->button2, gtk_widget_destroy);
g_clear_pointer (&self->button3, gtk_widget_destroy);
GTK_WIDGET_CLASS (vfl_grid_parent_class)->destroy (widget);
}
static void
vfl_grid_class_init (VflGridClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->destroy = vfl_grid_destroy;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
}
/* Layout:
*
* +-----------------------------+
* | +-----------+ +-----------+ |
* | | Child 1 | | Child 2 | |
* | +-----------+ +-----------+ |
* | +-------------------------+ |
* | | Child 3 | |
* | +-------------------------+ |
* +-----------------------------+
*
* Constraints:
*
* super.start = child1.start - 8
* child1.width = child2.width
* child1.end = child2.start - 12
* child2.end = super.end - 8
* super.start = child3.start - 8
* child3.end = super.end - 8
* super.top = child1.top - 8
* super.top = child2.top - 8
* child1.bottom = child3.top - 12
* child2.bottom = child3.top - 12
* child3.height = child1.height
* child3.height = child2.height
* child3.bottom = super.bottom - 8
*
* Visual format:
*
* H:|-8-[view1(==view2)-12-[view2]-8-|
* H:|-8-[view3]-8-|
* V:|-8-[view1]-12-[view3(==view1)]-8-|
* V:|-8-[view2]-12-[view3(==view2)]-8-|
*/
static void
build_constraints (VflGrid *self,
GtkConstraintLayout *manager)
{
const char * const vfl[] = {
"H:|-[button1(==button2)]-12-[button2]-|",
"H:|-[button3]-|",
"V:|-[button1]-12-[button3(==button1)]-|",
"V:|-[button2]-12-[button3(==button2)]-|",
};
GError *error = NULL;
gtk_constraint_layout_add_constraints_from_description (manager, vfl, G_N_ELEMENTS (vfl),
8, 8,
&error,
"button1", self->button1,
"button2", self->button2,
"button3", self->button3,
NULL);
if (error != NULL)
{
g_printerr ("VFL parsing error:\n%s", error->message);
g_error_free (error);
}
}
static void
vfl_grid_init (VflGrid *self)
{
GtkWidget *widget = 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");
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
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 (vfl_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

@@ -150,6 +150,9 @@
<file>clipboard.c</file>
<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

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

View File

@@ -1,3 +1,4 @@
subdir('constraint-editor')
subdir('gtk-demo')
subdir('icon-browser')
subdir('node-editor')

View File

@@ -82,6 +82,8 @@
#include <gtk/gtkcolorutils.h>
#include <gtk/gtkcombobox.h>
#include <gtk/gtkcomboboxtext.h>
#include <gtk/gtkconstraintlayout.h>
#include <gtk/gtkconstraint.h>
#include <gtk/gtkcontainer.h>
#include <gtk/gtkcssprovider.h>
#include <gtk/gtkcustomlayout.h>

View File

@@ -561,13 +561,6 @@ prop_actions_connect (GtkActionMuxer *muxer)
}
}
static void
prop_actions_disconnect (GtkActionMuxer *muxer)
{
if (muxer->widget)
g_signal_handlers_disconnect_by_func (muxer->widget,
prop_action_notify, muxer);
}
static gboolean
gtk_action_muxer_query_action (GActionGroup *action_group,
@@ -819,8 +812,6 @@ gtk_action_muxer_dispose (GObject *object)
{
GtkActionMuxer *muxer = GTK_ACTION_MUXER (object);
prop_actions_disconnect (muxer);
if (muxer->parent)
{
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer);

617
gtk/gtkconstraint.c Normal file
View File

@@ -0,0 +1,617 @@
/* gtkconstraint.c: Constraint between two widgets
* Copyright 2019 GNOME Foundation
*
* 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: Emmanuele Bassi
*/
/**
* SECTION:gtkconstraint
* @Title: GtkConstraint
* @Short_description: The description of a constraint
*
* #GtkConstraint describes a constraint between an attribute on a widget
* and another attribute on another widget, expressed as a linear equation
* like:
*
* |[
* target.attr1 = source.attr2 × multiplier + constant
* ]|
*
* Each #GtkConstraint is part of a system that will be solved by a
* #GtkConstraintLayout in order to allocate and position each child widget.
*
* The source and target widgets, as well as their attributes, of a
* #GtkConstraint instance are immutable after creation.
*/
#include "config.h"
#include "gtkconstraintprivate.h"
#include "gtkconstraintsolverprivate.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
#include "gtkwidget.h"
enum {
PROP_TARGET = 1,
PROP_TARGET_ATTRIBUTE,
PROP_RELATION,
PROP_SOURCE,
PROP_SOURCE_ATTRIBUTE,
PROP_MULTIPLIER,
PROP_CONSTANT,
PROP_STRENGTH,
N_PROPERTIES
};
static GParamSpec *obj_props[N_PROPERTIES];
G_DEFINE_TYPE (GtkConstraint, gtk_constraint, G_TYPE_OBJECT)
static void
gtk_constraint_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkConstraint *self = GTK_CONSTRAINT (gobject);
switch (prop_id)
{
case PROP_TARGET:
self->target = g_value_get_object (value);
break;
case PROP_TARGET_ATTRIBUTE:
self->target_attribute = g_value_get_enum (value);
break;
case PROP_RELATION:
self->relation = g_value_get_enum (value);
break;
case PROP_SOURCE:
self->source = g_value_get_object (value);
break;
case PROP_SOURCE_ATTRIBUTE:
self->source_attribute = g_value_get_enum (value);
break;
case PROP_MULTIPLIER:
self->multiplier = g_value_get_double (value);
break;
case PROP_CONSTANT:
self->constant = g_value_get_double (value);
break;
case PROP_STRENGTH:
self->strength = g_value_get_int (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)
{
GtkConstraint *self = GTK_CONSTRAINT (gobject);
switch (prop_id)
{
case PROP_TARGET:
g_value_set_object (value, self->target);
break;
case PROP_TARGET_ATTRIBUTE:
g_value_set_enum (value, self->target_attribute);
break;
case PROP_RELATION:
g_value_set_enum (value, self->relation);
break;
case PROP_SOURCE:
g_value_set_object (value, self->source);
break;
case PROP_SOURCE_ATTRIBUTE:
g_value_set_enum (value, self->source_attribute);
break;
case PROP_MULTIPLIER:
g_value_set_double (value, self->multiplier);
break;
case PROP_CONSTANT:
g_value_set_double (value, self->constant);
break;
case PROP_STRENGTH:
g_value_set_int (value, self->strength);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_constraint_finalize (GObject *gobject)
{
GtkConstraint *self = GTK_CONSTRAINT (gobject);
gtk_constraint_detach (self);
G_OBJECT_CLASS (gtk_constraint_parent_class)->finalize (gobject);
}
static void
gtk_constraint_class_init (GtkConstraintClass *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;
/**
* GtkConstraint:target:
*
* The target of the constraint.
*
* The constraint will set the #GtkConstraint:target-attribute of the
* target using the #GtkConstraint:source-attribute of the source
* widget.
*/
obj_props[PROP_TARGET] =
g_param_spec_object ("target",
P_("Target"),
P_("The target of the constraint"),
GTK_TYPE_CONSTRAINT_TARGET,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:target-attribute:
*
* The attribute of the #GtkConstraint:target set by the constraint.
*/
obj_props[PROP_TARGET_ATTRIBUTE] =
g_param_spec_enum ("target-attribute",
P_("Target Attribute"),
P_("The attribute of the target set by the constraint"),
GTK_TYPE_CONSTRAINT_ATTRIBUTE,
GTK_CONSTRAINT_ATTRIBUTE_NONE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:relation:
*
* The relation order between the terms of the constraint.
*/
obj_props[PROP_RELATION] =
g_param_spec_enum ("relation",
P_("Relation"),
P_("The relation between the source and target attributes"),
GTK_TYPE_CONSTRAINT_RELATION,
GTK_CONSTRAINT_RELATION_EQ,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:source:
*
* The source of the constraint.
*
* The constraint will set the #GtkConstraint:target-attribute of the
* target using the #GtkConstraint:source-attribute of the source.
*/
obj_props[PROP_SOURCE] =
g_param_spec_object ("source",
P_("Source"),
P_("The source of the constraint"),
GTK_TYPE_CONSTRAINT_TARGET,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:source-attribute:
*
* The attribute of the #GtkConstraint:source read by the constraint.
*/
obj_props[PROP_SOURCE_ATTRIBUTE] =
g_param_spec_enum ("source-attribute",
P_("Source Attribute"),
P_("The attribute of the source widget set by the constraint"),
GTK_TYPE_CONSTRAINT_ATTRIBUTE,
GTK_CONSTRAINT_ATTRIBUTE_NONE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:multiplier:
*
* The multiplication factor to be applied to the
* #GtkConstraint:source-attribue.
*/
obj_props[PROP_MULTIPLIER] =
g_param_spec_double ("multiplier",
P_("Multiplier"),
P_("The multiplication factor to be applied to the source attribute"),
-G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:constant:
*
* The constant value to be added to the #GtkConstraint:source-attribute.
*/
obj_props[PROP_CONSTANT] =
g_param_spec_double ("constant",
P_("Constant"),
P_("The constant to be added to the source attribute"),
-G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
/**
* GtkConstraint:strength:
*
* The strength of the constraint.
*
* The strength can be expressed either using one of the symbolic values
* of the #GtkConstraintStrength enumeration, or any positive integer
* value.
*/
obj_props[PROP_STRENGTH] =
g_param_spec_int ("strength",
P_("Strength"),
P_("The strength of the constraint"),
GTK_CONSTRAINT_STRENGTH_WEAK, G_MAXINT,
GTK_CONSTRAINT_STRENGTH_REQUIRED,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_props);
}
static void
gtk_constraint_init (GtkConstraint *self)
{
self->multiplier = 1.0;
self->constant = 0.0;
self->target_attribute = GTK_CONSTRAINT_ATTRIBUTE_NONE;
self->source_attribute = GTK_CONSTRAINT_ATTRIBUTE_NONE;
self->relation = GTK_CONSTRAINT_RELATION_EQ;
self->strength = GTK_CONSTRAINT_STRENGTH_REQUIRED;
}
/**
* gtk_constraint_new:
* @target: (nullable) (type GtkConstraintTarget): a #GtkConstraintTarget
* @target_attribute: the attribute of @target to be set
* @relation: the relation equivalence between @target_attribute and @source_attribute
* @source: (nullable) (type GtkConstraintTarget): a #GtkConstraintTarget
* @source_attribute: the attribute of @source to be read
* @multiplier: a multiplication factor to be applied to @source_attribute
* @constant: a constant factor to be added to @source_attribute
* @strength: the strength of the constraint
*
* Creates a new #GtkConstraint representing a relation between a layout
* attribute on a source and a layout attribute on a target.
*
* Returns: the newly created #GtkConstraint
*/
GtkConstraint *
gtk_constraint_new (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
gpointer source,
GtkConstraintAttribute source_attribute,
double multiplier,
double constant,
int strength)
{
g_return_val_if_fail (target == NULL || GTK_IS_CONSTRAINT_TARGET (target), NULL);
g_return_val_if_fail (source == NULL || GTK_IS_CONSTRAINT_TARGET (source), NULL);
return g_object_new (GTK_TYPE_CONSTRAINT,
"target", target,
"target-attribute", target_attribute,
"relation", relation,
"source", source,
"source-attribute", source_attribute,
"multiplier", multiplier,
"constant", constant,
"strength", strength,
NULL);
}
/**
* gtk_constraint_new_constant:
* @target: (nullable) (type GtkConstraintTarget): a #GtkConstraintTarget
* @target_attribute: the attribute of @target to be set
* @relation: the relation equivalence between @target_attribute and @constant
* @constant: a constant factor to be set on @target_attribute
* @strength: the strength of the constraint
*
* Creates a new #GtkConstraint representing a relation between a layout
* attribute on a target and a constant value.
*
* Returns: the newly created #GtkConstraint
*/
GtkConstraint *
gtk_constraint_new_constant (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
double constant,
int strength)
{
g_return_val_if_fail (target == NULL || GTK_IS_CONSTRAINT_TARGET (target), NULL);
return g_object_new (GTK_TYPE_CONSTRAINT,
"target", target,
"target-attribute", target_attribute,
"relation", relation,
"source-attribute", GTK_CONSTRAINT_ATTRIBUTE_NONE,
"constant", constant,
"strength", strength,
NULL);
}
/**
* gtk_constraint_get_target_widget:
* @constraint: a #GtkConstraint
*
* Retrieves the target widget for the @constraint.
*
* Returns: (transfer none) (nullable): a #GtkWidget
*/
GtkWidget *
gtk_constraint_get_target_widget (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
if (GTK_IS_WIDGET (constraint->target))
return GTK_WIDGET (constraint->target);
return NULL;
}
GtkConstraintTarget *
gtk_constraint_get_target (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
return constraint->target;
}
GtkConstraintAttribute
gtk_constraint_get_target_attribute (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_ATTRIBUTE_NONE);
return constraint->target_attribute;
}
/**
* gtk_constraint_get_source_widget:
* @constraint: a #GtkConstraint
*
* Retrieves the source widget for the @constraint.
*
* Returns: (transfer none) (nullable): a #GtkWidget
*/
GtkWidget *
gtk_constraint_get_source_widget (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
if (GTK_IS_WIDGET (constraint->source))
return GTK_WIDGET (constraint->source);
return NULL;
}
GtkConstraintTarget *
gtk_constraint_get_source (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
return constraint->source;
}
GtkConstraintAttribute
gtk_constraint_get_source_attribute (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_ATTRIBUTE_NONE);
return constraint->source_attribute;
}
GtkConstraintRelation
gtk_constraint_get_relation (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_RELATION_EQ);
return constraint->relation;
}
double
gtk_constraint_get_multiplier (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), 1.0);
return constraint->multiplier;
}
double
gtk_constraint_get_constant (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), 0.0);
return constraint->constant;
}
int
gtk_constraint_get_strength (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_STRENGTH_REQUIRED);
return constraint->strength;
}
/*< private >
* gtk_constraint_get_weight:
* @constraint: a #GtkConstraint
*
* Computes the weight of the @constraint to be used with
* #GtkConstraintSolver.
*
* Returns: the weight of the constraint
*/
double
gtk_constraint_get_weight (GtkConstraint *constraint)
{
if (constraint->strength > 0)
return constraint->strength;
switch (constraint->strength)
{
case GTK_CONSTRAINT_STRENGTH_REQUIRED:
return GTK_CONSTRAINT_WEIGHT_REQUIRED;
case GTK_CONSTRAINT_STRENGTH_STRONG:
return GTK_CONSTRAINT_WEIGHT_STRONG;
case GTK_CONSTRAINT_STRENGTH_MEDIUM:
return GTK_CONSTRAINT_WEIGHT_MEDIUM;
case GTK_CONSTRAINT_STRENGTH_WEAK:
return GTK_CONSTRAINT_WEIGHT_WEAK;
default:
g_assert_not_reached ();
}
return 0;
}
/**
* gtk_constraint_is_required:
* @constraint: a #GtkConstraint
*
* Checks whether the @constraint is a required relation for solving the
* constraint layout.
*
* Returns: %TRUE if the constraint is required
*/
gboolean
gtk_constraint_is_required (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
return constraint->strength == GTK_CONSTRAINT_STRENGTH_REQUIRED;
}
/**
* gtk_constraint_is_attached:
* @constraint: a #GtkConstraint
*
* Checks whether the @constraint is attached to a #GtkConstraintLayout,
* and it is contributing to the layout.
*
* Returns: %TRUE if the constraint is attached
*/
gboolean
gtk_constraint_is_attached (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
return constraint->constraint_ref != NULL;
}
/**
* gtk_constraint_is_constant:
* @constraint: a #GtkConstraint
*
* Checks whether the @constraint describes a relation between an attribute
* on the #GtkConstraint:target-widget and a constant value.
*
* Returns: %TRUE if the constraint is a constant relation
*/
gboolean
gtk_constraint_is_constant (GtkConstraint *constraint)
{
g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
return constraint->source == NULL &&
constraint->source_attribute == GTK_CONSTRAINT_ATTRIBUTE_NONE;
}
void
gtk_constraint_attach (GtkConstraint *constraint,
GtkConstraintSolver *solver,
GtkConstraintRef *ref)
{
g_return_if_fail (GTK_IS_CONSTRAINT (constraint));
g_return_if_fail (GTK_IS_CONSTRAINT_SOLVER (solver));
g_return_if_fail (ref != NULL);
constraint->constraint_ref = ref;
constraint->solver = solver;
}
void
gtk_constraint_detach (GtkConstraint *constraint)
{
g_return_if_fail (GTK_IS_CONSTRAINT (constraint));
if (constraint->constraint_ref == NULL)
return;
gtk_constraint_solver_remove_constraint (constraint->solver, constraint->constraint_ref);
constraint->constraint_ref = NULL;
constraint->solver = NULL;
}
typedef struct _GtkConstraintTargetInterface GtkConstraintTargetInterface;
struct _GtkConstraintTargetInterface
{
GTypeInterface g_iface;
};
G_DEFINE_INTERFACE (GtkConstraintTarget, gtk_constraint_target, G_TYPE_OBJECT)
static void
gtk_constraint_target_default_init (GtkConstraintTargetInterface *iface)
{
}

104
gtk/gtkconstraint.h Normal file
View File

@@ -0,0 +1,104 @@
/* gtkconstraint.h: Constraint between two widgets
* Copyright 2019 GNOME Foundation
*
* 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: Emmanuele Bassi
*/
#pragma once
#include <gtk/gtktypes.h>
#include <gtk/gtkenums.h>
G_BEGIN_DECLS
typedef struct _GtkConstraintTarget GtkConstraintTarget;
#define GTK_TYPE_CONSTRAINT_TARGET (gtk_constraint_target_get_type ())
/**
* GtkConstraintTarget:
*
* The GtkConstraintTarget interface is implemented by objects that
* can be used as source or target in #GtkConstraints. Besides
* #GtkWidget, it is also implemented by #GtkConstraintGuide.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GtkConstraintTarget, gtk_constraint_target, GTK, CONSTRAINT_TARGET, GObject)
#define GTK_TYPE_CONSTRAINT (gtk_constraint_get_type ())
/**
* GtkConstraint:
*
* An object describing the relation between two widget attributes.
*
* All relations are in the form:
*
* |[<!-- language=plain -->
* target.attr_name = source.attr_name × multiplier + constant
* ]|
*
* A #GtkConstraint is immutable once it's created.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraint, gtk_constraint, GTK, CONSTRAINT, GObject)
GDK_AVAILABLE_IN_ALL
GtkConstraint * gtk_constraint_new (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
gpointer source,
GtkConstraintAttribute source_attribute,
double multiplier,
double constant,
int strength);
GDK_AVAILABLE_IN_ALL
GtkConstraint * gtk_constraint_new_constant (gpointer target,
GtkConstraintAttribute target_attribute,
GtkConstraintRelation relation,
double constant,
int strength);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_constraint_get_target_widget (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintTarget * gtk_constraint_get_target (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintAttribute gtk_constraint_get_target_attribute (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_constraint_get_source_widget (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintTarget * gtk_constraint_get_source (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintAttribute gtk_constraint_get_source_attribute (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
GtkConstraintRelation gtk_constraint_get_relation (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
double gtk_constraint_get_multiplier (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
double gtk_constraint_get_constant (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
int gtk_constraint_get_strength (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
gboolean gtk_constraint_is_required (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
gboolean gtk_constraint_is_attached (GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
gboolean gtk_constraint_is_constant (GtkConstraint *constraint);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
/* gtkconstraintequationprivate.h: Constraint expressions and variables
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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: Emmanuele Bassi
*/
#pragma once
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
GtkConstraintVariable *
gtk_constraint_variable_new (const char *prefix,
const char *name);
GtkConstraintVariable *
gtk_constraint_variable_new_dummy (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_new_objective (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_new_slack (const char *name);
GtkConstraintVariable *
gtk_constraint_variable_ref (GtkConstraintVariable *variable);
void
gtk_constraint_variable_unref (GtkConstraintVariable *variable);
void
gtk_constraint_variable_set_value (GtkConstraintVariable *variable,
double value);
double
gtk_constraint_variable_get_value (const GtkConstraintVariable *variable);
char *
gtk_constraint_variable_to_string (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_external (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_pivotable (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_restricted (const GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_is_dummy (const GtkConstraintVariable *variable);
typedef struct {
GtkConstraintVariable *first;
GtkConstraintVariable *second;
} GtkConstraintVariablePair;
GtkConstraintVariablePair *
gtk_constraint_variable_pair_new (GtkConstraintVariable *first,
GtkConstraintVariable *second);
void
gtk_constraint_variable_pair_free (GtkConstraintVariablePair *pair);
typedef struct _GtkConstraintVariableSet GtkConstraintVariableSet;
GtkConstraintVariableSet *
gtk_constraint_variable_set_new (void);
void
gtk_constraint_variable_set_free (GtkConstraintVariableSet *set);
gboolean
gtk_constraint_variable_set_add (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_variable_set_remove (GtkConstraintVariableSet *set,
GtkConstraintVariable *variable);
int
gtk_constraint_variable_set_size (GtkConstraintVariableSet *set);
typedef struct {
/*< private >*/
gpointer dummy1;
gpointer dummy2;
gint64 dummy3;
} GtkConstraintVariableSetIter;
void
gtk_constraint_variable_set_iter_init (GtkConstraintVariableSetIter *iter,
GtkConstraintVariableSet *set);
gboolean
gtk_constraint_variable_set_iter_next (GtkConstraintVariableSetIter *iter,
GtkConstraintVariable **variable_p);
GtkConstraintExpression *
gtk_constraint_expression_new (double constant);
GtkConstraintExpression *
gtk_constraint_expression_new_from_variable (GtkConstraintVariable *variable);
GtkConstraintExpression *
gtk_constraint_expression_ref (GtkConstraintExpression *expression);
void
gtk_constraint_expression_unref (GtkConstraintExpression *expression);
GtkConstraintExpression *
gtk_constraint_expression_clone (GtkConstraintExpression *expression);
void
gtk_constraint_expression_set_constant (GtkConstraintExpression *expression,
double constant);
double
gtk_constraint_expression_get_constant (const GtkConstraintExpression *expression);
gboolean
gtk_constraint_expression_is_constant (const GtkConstraintExpression *expression);
void
gtk_constraint_expression_add_expression (GtkConstraintExpression *a_expr,
GtkConstraintExpression *b_expr,
double n,
GtkConstraintVariable *subject,
GtkConstraintSolver *solver);
void
gtk_constraint_expression_add_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable,
double coefficient,
GtkConstraintVariable *subject,
GtkConstraintSolver *solver);
void
gtk_constraint_expression_remove_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
void
gtk_constraint_expression_set_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable,
double coefficient);
double
gtk_constraint_expression_get_coefficient (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
char *
gtk_constraint_expression_to_string (const GtkConstraintExpression *expression);
double
gtk_constraint_expression_new_subject (GtkConstraintExpression *expression,
GtkConstraintVariable *subject);
void
gtk_constraint_expression_change_subject (GtkConstraintExpression *expression,
GtkConstraintVariable *old_subject,
GtkConstraintVariable *new_subject);
void
gtk_constraint_expression_substitute_out (GtkConstraintExpression *expression,
GtkConstraintVariable *out_var,
GtkConstraintExpression *expr,
GtkConstraintVariable *subject,
GtkConstraintSolver *solver);
GtkConstraintVariable *
gtk_constraint_expression_get_pivotable_variable (GtkConstraintExpression *expression);
GtkConstraintExpression *
gtk_constraint_expression_plus_constant (GtkConstraintExpression *expression,
double constant);
GtkConstraintExpression *
gtk_constraint_expression_minus_constant (GtkConstraintExpression *expression,
double constant);
GtkConstraintExpression *
gtk_constraint_expression_plus_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
GtkConstraintExpression *
gtk_constraint_expression_minus_variable (GtkConstraintExpression *expression,
GtkConstraintVariable *variable);
GtkConstraintExpression *
gtk_constraint_expression_multiply_by (GtkConstraintExpression *expression,
double factor);
GtkConstraintExpression *
gtk_constraint_expression_divide_by (GtkConstraintExpression *expression,
double factor);
struct _GtkConstraintExpressionBuilder
{
/*< private >*/
gpointer dummy1;
gpointer dummy2;
int dummy3;
};
void
gtk_constraint_expression_builder_init (GtkConstraintExpressionBuilder *builder,
GtkConstraintSolver *solver);
void
gtk_constraint_expression_builder_term (GtkConstraintExpressionBuilder *builder,
GtkConstraintVariable *term);
void
gtk_constraint_expression_builder_plus (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_minus (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_divide_by (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_multiply_by (GtkConstraintExpressionBuilder *builder);
void
gtk_constraint_expression_builder_constant (GtkConstraintExpressionBuilder *builder,
double value);
GtkConstraintExpression *
gtk_constraint_expression_builder_finish (GtkConstraintExpressionBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
/*< private >
* GtkConstraintExpressionIter:
*
* An iterator object for terms inside a #GtkConstraintExpression.
*/
typedef struct {
/*< private >*/
gpointer dummy1;
gpointer dummy2;
gint64 dummy3;
} GtkConstraintExpressionIter;
void
gtk_constraint_expression_iter_init (GtkConstraintExpressionIter *iter,
GtkConstraintExpression *equation);
gboolean
gtk_constraint_expression_iter_next (GtkConstraintExpressionIter *iter,
GtkConstraintVariable **variable,
double *coefficient);
gboolean
gtk_constraint_expression_iter_prev (GtkConstraintExpressionIter *iter,
GtkConstraintVariable **variable,
double *coefficient);
G_END_DECLS

1912
gtk/gtkconstraintlayout.c Normal file

File diff suppressed because it is too large Load Diff

100
gtk/gtkconstraintlayout.h Normal file
View File

@@ -0,0 +1,100 @@
/* gtkconstraintlayout.h: Layout manager using constraints
* Copyright 2019 GNOME Foundation
*
* 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: Emmanuele Bassi
*/
#pragma once
#include <gtk/gtklayoutmanager.h>
#include <gtk/gtkconstraint.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:
*
* A #GtkLayoutChild in a #GtkConstraintLayout.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK, CONSTRAINT_LAYOUT_CHILD, GtkLayoutChild)
/**
* GtkConstraintGuide:
*
* An object that can be added to a #GtkConstraintLayout and be
* used in constraints like a widget, without being drawn. Guides
* have a minimal and natural size. Depending on the constraints
* that are applied, they can act like a guideline that widgets
* can be aligned to, or like 'flexible space'.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraintGuide, gtk_constraint_guide, GTK, CONSTRAINT_GUIDE, GObject)
GDK_AVAILABLE_IN_ALL
GtkConstraintGuide * gtk_constraint_guide_new (void);
/**
* GtkConstraintLayout:
*
* A layout manager using #GtkConstraint to describe
* relations between widgets.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkConstraintLayout, gtk_constraint_layout, GTK, CONSTRAINT_LAYOUT, GtkLayoutManager)
GDK_AVAILABLE_IN_ALL
GtkLayoutManager * gtk_constraint_layout_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_add_constraint (GtkConstraintLayout *manager,
GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_remove_constraint (GtkConstraintLayout *manager,
GtkConstraint *constraint);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_add_guide (GtkConstraintLayout *manager,
GtkConstraintGuide *guide);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_remove_guide (GtkConstraintLayout *manager,
GtkConstraintGuide *guide);
GDK_AVAILABLE_IN_ALL
gboolean gtk_constraint_layout_add_constraints_from_description (GtkConstraintLayout *manager,
const char * const lines[],
gsize n_lines,
int hspacing,
int vspacing,
GError **error,
const char *first_view,
...) G_GNUC_NULL_TERMINATED;
GDK_AVAILABLE_IN_ALL
gboolean gtk_constraint_layout_add_constraints_from_descriptionv (GtkConstraintLayout *manager,
const char * const lines[],
gsize n_lines,
int hspacing,
int vspacing,
GHashTable *views,
GError **error);
GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_remove_all_constraints (GtkConstraintLayout *manager);
G_END_DECLS

View File

@@ -0,0 +1,62 @@
/* gtkconstraintprivate.h: Constraint between two widgets
* Copyright 2019 GNOME Foundation
*
* 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: Emmanuele Bassi
*/
#pragma once
#include "gtkconstraint.h"
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
struct _GtkConstraint
{
GObject parent_instance;
GtkConstraintAttribute target_attribute;
GtkConstraintAttribute source_attribute;
GtkConstraintTarget *target;
GtkConstraintTarget *source;
GtkConstraintRelation relation;
double multiplier;
double constant;
int strength;
/* A reference to the real constraint inside the
* GtkConstraintSolver, so we can remove it when
* finalizing the GtkConstraint instance
*/
GtkConstraintRef *constraint_ref;
GtkConstraintSolver *solver;
guint active : 1;
};
double gtk_constraint_get_weight (GtkConstraint *constraint);
void gtk_constraint_attach (GtkConstraint *constraint,
GtkConstraintSolver *solver,
GtkConstraintRef *ref);
void gtk_constraint_detach (GtkConstraint *constraint);
G_END_DECLS

2207
gtk/gtkconstraintsolver.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/* gtkconstraintsolverprivate.h: Constraint solver based on the Cassowary method
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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: Emmanuele Bassi
*/
#pragma once
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
#define GTK_TYPE_CONSTRAINT_SOLVER (gtk_constraint_solver_get_type())
G_DECLARE_FINAL_TYPE (GtkConstraintSolver, gtk_constraint_solver, GTK, CONSTRAINT_SOLVER, GObject)
/* Symbolic weight thresholds
*
* Constraint weights live on a continuum, but we use thresholds for simplicity's
* sake, so we don't have to necessarily reason in terms of numeric values.
*
* The public API has a similar approach, where the symbolic constants are negative
* values, and positive values are explicit weights. We map those values into
* numeric values that the GtkConstraintSolver can plug into the linear equations
* tableau.
*/
#define GTK_CONSTRAINT_WEIGHT_REQUIRED (make_weight (1000, 1000, 1000, 1))
#define GTK_CONSTRAINT_WEIGHT_STRONG (make_weight ( 1, 0, 0, 1))
#define GTK_CONSTRAINT_WEIGHT_MEDIUM (make_weight ( 0, 1, 0, 1))
#define GTK_CONSTRAINT_WEIGHT_WEAK (make_weight ( 0, 0, 1, 1))
G_GNUC_PURE
static inline double
make_weight (double a,
double b,
double c,
double w)
{
double res = 0;
res += CLAMP (a * w, 0, 1000) * 1000000;
res += CLAMP (b * w, 0, 1000) * 1000;
res += CLAMP (c * w, 0, 1000);
return res;
}
GtkConstraintSolver *
gtk_constraint_solver_new (void);
void
gtk_constraint_solver_freeze (GtkConstraintSolver *solver);
void
gtk_constraint_solver_thaw (GtkConstraintSolver *solver);
void
gtk_constraint_solver_resolve (GtkConstraintSolver *solver);
GtkConstraintVariable *
gtk_constraint_solver_create_variable (GtkConstraintSolver *solver,
const char *prefix,
const char *name,
double value);
GtkConstraintRef *
gtk_constraint_solver_add_constraint (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
GtkConstraintRelation relation,
GtkConstraintExpression *expression,
double strength);
void
gtk_constraint_solver_remove_constraint (GtkConstraintSolver *solver,
GtkConstraintRef *reference);
GtkConstraintRef *
gtk_constraint_solver_add_stay_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
double strength);
void
gtk_constraint_solver_remove_stay_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_solver_has_stay_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
GtkConstraintRef *
gtk_constraint_solver_add_edit_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
double strength);
void
gtk_constraint_solver_remove_edit_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
gboolean
gtk_constraint_solver_has_edit_variable (GtkConstraintSolver *solver,
GtkConstraintVariable *variable);
void
gtk_constraint_solver_suggest_value (GtkConstraintSolver *solver,
GtkConstraintVariable *variable,
double value);
void
gtk_constraint_solver_begin_edit (GtkConstraintSolver *solver);
void
gtk_constraint_solver_end_edit (GtkConstraintSolver *solver);
void
gtk_constraint_solver_note_added_variable (GtkConstraintSolver *self,
GtkConstraintVariable *variable,
GtkConstraintVariable *subject);
void
gtk_constraint_solver_note_removed_variable (GtkConstraintSolver *self,
GtkConstraintVariable *variable,
GtkConstraintVariable *subject);
void
gtk_constraint_solver_clear (GtkConstraintSolver *solver);
char *
gtk_constraint_solver_to_string (GtkConstraintSolver *solver);
G_END_DECLS

View File

@@ -0,0 +1,50 @@
/* gtkconstrainttypesprivate.h: Private types for the constraint solver
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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: Emmanuele Bassi
*/
#pragma once
#include <gtk/gtktypes.h>
#include <gtk/gtkenums.h>
G_BEGIN_DECLS
typedef struct _GtkConstraintVariable GtkConstraintVariable;
typedef struct _GtkConstraintExpression GtkConstraintExpression;
typedef struct _GtkConstraintExpressionBuilder GtkConstraintExpressionBuilder;
/*< private >
* GtkConstraintRef:
*
* A reference to a constraint stored inside the solver; while #GtkConstraint
* represent the public API, a #GtkConstraintRef represents data stored inside
* the solver. A #GtkConstraintRef is completely opaque, and should only be
* used to remove a constraint from the solver.
*/
typedef struct _GtkConstraintRef GtkConstraintRef;
/*< private >
* GtkConstraintSolver:
*
* A simplex solver using the Cassowary constraint solving algorithm.
*/
typedef struct _GtkConstraintSolver GtkConstraintSolver;
G_END_DECLS

1226
gtk/gtkconstraintvflparser.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
/* gtkconstraintvflparserprivate.h: VFL constraint definition parser
*
* Copyright 2017 Endless
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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/>.
*/
#pragma once
#include "gtkconstrainttypesprivate.h"
G_BEGIN_DECLS
#define GTK_CONSTRAINT_VFL_PARSER_ERROR (gtk_constraint_vfl_parser_error_quark ())
typedef enum {
GTK_CONSTRAINT_VFL_PARSER_ERROR_INVALID_SYMBOL,
GTK_CONSTRAINT_VFL_PARSER_ERROR_INVALID_ATTRIBUTE,
GTK_CONSTRAINT_VFL_PARSER_ERROR_INVALID_VIEW,
GTK_CONSTRAINT_VFL_PARSER_ERROR_INVALID_METRIC,
GTK_CONSTRAINT_VFL_PARSER_ERROR_INVALID_PRIORITY,
GTK_CONSTRAINT_VFL_PARSER_ERROR_INVALID_RELATION
} VflError;
typedef struct _GtkConstraintVflParser GtkConstraintVflParser;
typedef struct {
const char *view1;
const char *attr1;
GtkConstraintRelation relation;
const char *view2;
const char *attr2;
double constant;
double multiplier;
double strength;
} GtkConstraintVfl;
GQuark gtk_constraint_vfl_parser_error_quark (void);
GtkConstraintVflParser *
gtk_constraint_vfl_parser_new (void);
void
gtk_constraint_vfl_parser_free (GtkConstraintVflParser *parser);
void
gtk_constraint_vfl_parser_set_default_spacing (GtkConstraintVflParser *parser,
int hspacing,
int vspacing);
void
gtk_constraint_vfl_parser_set_metrics (GtkConstraintVflParser *parser,
GHashTable *metrics);
void
gtk_constraint_vfl_parser_set_views (GtkConstraintVflParser *parser,
GHashTable *views);
gboolean
gtk_constraint_vfl_parser_parse_line (GtkConstraintVflParser *parser,
const char *line,
gssize len,
GError **error);
int
gtk_constraint_vfl_parser_get_error_offset (GtkConstraintVflParser *parser);
int
gtk_constraint_vfl_parser_get_error_range (GtkConstraintVflParser *parser);
GtkConstraintVfl *
gtk_constraint_vfl_parser_get_constraints (GtkConstraintVflParser *parser,
int *n_constraints);
G_END_DECLS

View File

@@ -1053,4 +1053,78 @@ typedef enum {
GTK_PICK_NON_TARGETABLE = 1 << 1
} GtkPickFlags;
/**
* GtkConstraintRelation:
* @GTK_CONSTRAINT_RELATION_EQ: Equal
* @GTK_CONSTRAINT_RELATION_LE: Less than, or equal
* @GTK_CONSTRAINT_RELATION_GE: Greater than, or equal
*
* The relation between two terms of a constraint.
*/
typedef enum {
GTK_CONSTRAINT_RELATION_LE = -1,
GTK_CONSTRAINT_RELATION_EQ = 0,
GTK_CONSTRAINT_RELATION_GE = 1
} GtkConstraintRelation;
/**
* GtkConstraintStrength:
* @GTK_CONSTRAINT_STRENGTH_REQUIRED: The constraint is required towards solving the layout
* @GTK_CONSTRAINT_STRENGTH_STRONG: A strong constraint
* @GTK_CONSTRAINT_STRENGTH_MEDIUM: A medium constraint
* @GTK_CONSTRAINT_STRENGTH_WEAK: A weak constraint
*
* The strength of a constraint, expressed as a symbolic constant.
*
* The strength of a #GtkConstraint can be expressed with any positive
* integer; the values of this enumeration can be used for readability.
*/
typedef enum {
GTK_CONSTRAINT_STRENGTH_REQUIRED = 0,
GTK_CONSTRAINT_STRENGTH_STRONG = -1,
GTK_CONSTRAINT_STRENGTH_MEDIUM = -2,
GTK_CONSTRAINT_STRENGTH_WEAK = -3
} GtkConstraintStrength;
/**
* GtkConstraintAttribute:
* @GTK_CONSTRAINT_ATTRIBUTE_NONE: No attribute, used for constant
* relations
* @GTK_CONSTRAINT_ATTRIBUTE_LEFT: The left edge of a widget, regardless of
* text direction
* @GTK_CONSTRAINT_ATTRIBUTE_RIGHT: The right edge of a widget, regardless
* of text direction
* @GTK_CONSTRAINT_ATTRIBUTE_TOP: The top edge of a widget
* @GTK_CONSTRAINT_ATTRIBUTE_BOTTOM: The bottom edge of a widget
* @GTK_CONSTRAINT_ATTRIBUTE_START: The leading edge of a widget, depending
* on text direction; equivalent to %GTK_CONSTRAINT_ATTRIBUTE_LEFT for LTR
* languages, and %GTK_CONSTRAINT_ATTRIBUTE_RIGHT for RTL ones
* @GTK_CONSTRAINT_ATTRIBUTE_END: The trailing edge of a widget, depending
* on text direction; equivalent to %GTK_CONSTRAINT_ATTRIBUTE_RIGHT for LTR
* languages, and %GTK_CONSTRAINT_ATTRIBUTE_LEFT for RTL ones
* @GTK_CONSTRAINT_ATTRIBUTE_WIDTH: The width of a widget
* @GTK_CONSTRAINT_ATTRIBUTE_HEIGHT: The height of a widget
* @GTK_CONSTRAINT_ATTRIBUTE_CENTER_X: The center of a widget, on the
* horizontal axis
* @GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y: The center of a widget, on the
* vertical axis
* @GTK_CONSTRAINT_ATTRIBUTE_BASELINE: The baseline of a widget
*
* The widget attributes that can be used when creating a #GtkConstraint.
*/
typedef enum {
GTK_CONSTRAINT_ATTRIBUTE_NONE,
GTK_CONSTRAINT_ATTRIBUTE_LEFT,
GTK_CONSTRAINT_ATTRIBUTE_RIGHT,
GTK_CONSTRAINT_ATTRIBUTE_TOP,
GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
GTK_CONSTRAINT_ATTRIBUTE_START,
GTK_CONSTRAINT_ATTRIBUTE_END,
GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
GTK_CONSTRAINT_ATTRIBUTE_CENTER_X,
GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y,
GTK_CONSTRAINT_ATTRIBUTE_BASELINE
} GtkConstraintAttribute;
#endif /* __GTK_ENUMS_H__ */

View File

@@ -91,6 +91,7 @@
typedef struct {
GtkWidget *widget;
GtkRoot *root;
/* HashTable<Widget, LayoutChild> */
GHashTable *layout_children;
@@ -98,6 +99,16 @@ typedef struct {
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutManager, gtk_layout_manager, G_TYPE_OBJECT)
static void
gtk_layout_manager_real_root (GtkLayoutManager *manager)
{
}
static void
gtk_layout_manager_real_unroot (GtkLayoutManager *manager)
{
}
static GtkSizeRequestMode
gtk_layout_manager_real_get_request_mode (GtkLayoutManager *manager,
GtkWidget *widget)
@@ -188,13 +199,30 @@ gtk_layout_manager_real_create_layout_child (GtkLayoutManager *manager,
NULL);
}
static void
gtk_layout_manager_finalize (GObject *gobject)
{
GtkLayoutManager *self = GTK_LAYOUT_MANAGER (gobject);
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (self);
g_clear_pointer (&priv->layout_children, g_hash_table_unref);
G_OBJECT_CLASS (gtk_layout_manager_parent_class)->finalize (gobject);
}
static void
gtk_layout_manager_class_init (GtkLayoutManagerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gtk_layout_manager_finalize;
klass->get_request_mode = gtk_layout_manager_real_get_request_mode;
klass->measure = gtk_layout_manager_real_measure;
klass->allocate = gtk_layout_manager_real_allocate;
klass->create_layout_child = gtk_layout_manager_real_create_layout_child;
klass->root = gtk_layout_manager_real_root;
klass->unroot = gtk_layout_manager_real_unroot;
}
static void
@@ -226,6 +254,38 @@ gtk_layout_manager_set_widget (GtkLayoutManager *layout_manager,
}
priv->widget = widget;
if (widget != NULL)
gtk_layout_manager_set_root (layout_manager, gtk_widget_get_root (widget));
}
/*< private >
* gtk_layout_manager_set_root:
* @layout_manager: a #GtkLayoutManager
* @root: (nullable): a #GtkWidget implementing #GtkRoot
*
* Sets a back pointer from @root to @layout_manager.
*
* This function is called by #GtkWidget when getting rooted and unrooted,
* and will call #GtkLayoutManagerClass.root() or #GtkLayoutManagerClass.unroot()
* depending on whether @root is a #GtkWidget or %NULL.
*/
void
gtk_layout_manager_set_root (GtkLayoutManager *layout_manager,
GtkRoot *root)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (layout_manager);
GtkRoot *old_root = priv->root;
priv->root = root;
if (old_root != root)
{
if (priv->root != NULL)
GTK_LAYOUT_MANAGER_GET_CLASS (layout_manager)->root (layout_manager);
else
GTK_LAYOUT_MANAGER_GET_CLASS (layout_manager)->unroot (layout_manager);
}
}
/**

View File

@@ -42,6 +42,10 @@ G_DECLARE_DERIVABLE_TYPE (GtkLayoutManager, gtk_layout_manager, GTK, LAYOUT_MANA
* @layout_child_type: the type of #GtkLayoutChild used by this layout manager
* @create_layout_child: a virtual function, used to create a #GtkLayoutChild
* meta object for the layout properties
* @root: a virtual function, called when the widget using the layout
* manager is attached to a #GtkRoot
* @unroot: a virtual function, called when the widget using the layout
* manager is detached from a #GtkRoot
*
* The `GtkLayoutManagerClass` structure contains only private data, and
* should only be accessed through the provided API, or when subclassing
@@ -77,6 +81,9 @@ struct _GtkLayoutManagerClass
GtkWidget *widget,
GtkWidget *for_child);
void (* root) (GtkLayoutManager *manager);
void (* unroot) (GtkLayoutManager *manager);
/*< private >*/
gpointer _padding[16];
};

View File

@@ -10,4 +10,7 @@ void gtk_layout_manager_set_widget (GtkLayoutManager *manager,
void gtk_layout_manager_remove_layout_child (GtkLayoutManager *manager,
GtkWidget *widget);
void gtk_layout_manager_set_root (GtkLayoutManager *manager,
GtkRoot *root);
G_END_DECLS

View File

@@ -50,10 +50,18 @@ gtk_root_default_get_display (GtkRoot *self)
return gdk_display_get_default ();
}
static GtkConstraintSolver *
gtk_root_default_get_constraint_solver (GtkRoot *self)
{
return NULL;
}
static void
gtk_root_default_init (GtkRootInterface *iface)
{
iface->get_display = gtk_root_default_get_display;
iface->get_constraint_solver = gtk_root_default_get_constraint_solver;
g_object_interface_install_property (iface,
g_param_spec_object ("focus-widget",
@@ -82,6 +90,17 @@ gtk_root_get_display (GtkRoot *self)
return iface->get_display (self);
}
GtkConstraintSolver *
gtk_root_get_constraint_solver (GtkRoot *self)
{
GtkRootInterface *iface;
g_return_val_if_fail (GTK_IS_ROOT (self), NULL);
iface = GTK_ROOT_GET_IFACE (self);
return iface->get_constraint_solver (self);
}
/**
* gtk_root_set_focus:
* @self: a #GtkRoot

View File

@@ -34,20 +34,6 @@ G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GtkRoot, gtk_root, GTK, ROOT, GtkWidget)
/**
* GtkRootIface:
*
* The list of functions that must be implemented for the #GtkRoot interface.
*/
struct _GtkRootInterface
{
/*< private >*/
GTypeInterface g_iface;
/*< public >*/
GdkDisplay * (* get_display) (GtkRoot *self);
};
GDK_AVAILABLE_IN_ALL
GdkDisplay * gtk_root_get_display (GtkRoot *self);

View File

@@ -3,8 +3,28 @@
#include "gtkroot.h"
#include "gtkconstraintsolverprivate.h"
G_BEGIN_DECLS
/**
* GtkRootIface:
*
* The list of functions that must be implemented for the #GtkRoot interface.
*/
struct _GtkRootInterface
{
/*< private >*/
GTypeInterface g_iface;
/*< public >*/
GdkDisplay * (* get_display) (GtkRoot *self);
GtkConstraintSolver * (* get_constraint_solver) (GtkRoot *self);
};
GtkConstraintSolver * gtk_root_get_constraint_solver (GtkRoot *self);
enum {
GTK_ROOT_PROP_FOCUS_WIDGET,
GTK_ROOT_NUM_PROPERTIES

View File

@@ -73,6 +73,7 @@
#include "gtkwindowgroup.h"
#include "gtkwindowprivate.h"
#include "gtknativeprivate.h"
#include "gtkconstraint.h"
#include "a11y/gtkwidgetaccessible.h"
#include "inspector/window.h"
@@ -770,6 +771,13 @@ gtk_widget_get_type (void)
NULL /* interface data */
};
const GInterfaceInfo constraint_target_info =
{
(GInterfaceInitFunc) NULL,
(GInterfaceFinalizeFunc) NULL,
NULL /* interface data */
};
widget_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED, g_intern_static_string ("GtkWidget"),
&widget_info, G_TYPE_FLAG_ABSTRACT);
@@ -782,6 +790,8 @@ gtk_widget_get_type (void)
&accessibility_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE,
&buildable_info) ;
g_type_add_interface_static (widget_type, GTK_TYPE_CONSTRAINT_TARGET,
&constraint_target_info) ;
}
return widget_type;
@@ -2893,6 +2903,9 @@ gtk_widget_root (GtkWidget *widget)
_gtk_widget_update_parent_muxer (widget);
if (priv->layout_manager)
gtk_layout_manager_set_root (priv->layout_manager, priv->root);
GTK_WIDGET_GET_CLASS (widget)->root (widget);
if (!GTK_IS_ROOT (widget))
@@ -2920,6 +2933,9 @@ gtk_widget_unroot (GtkWidget *widget)
if (priv->context)
gtk_style_context_set_display (priv->context, gdk_display_get_default ());
if (priv->layout_manager)
gtk_layout_manager_set_root (priv->layout_manager, NULL);
if (g_object_get_qdata (G_OBJECT (widget), quark_pango_context))
g_object_set_qdata (G_OBJECT (widget), quark_pango_context, NULL);

View File

@@ -281,6 +281,8 @@ typedef struct
GskRenderer *renderer;
GList *foci;
GtkConstraintSolver *constraint_solver;
} GtkWindowPrivate;
#ifdef GDK_WINDOWING_X11
@@ -1883,6 +1885,9 @@ gtk_window_init (GtkWindow *window)
g_signal_connect_swapped (priv->key_controller, "key-released",
G_CALLBACK (gtk_window_key_released), window);
gtk_widget_add_controller (widget, priv->key_controller);
/* Shared constraint solver */
priv->constraint_solver = gtk_constraint_solver_new ();
}
static GtkGesture *
@@ -2353,6 +2358,15 @@ gtk_window_native_get_renderer (GtkNative *native)
return priv->renderer;
}
static GtkConstraintSolver *
gtk_window_root_get_constraint_solver (GtkRoot *root)
{
GtkWindow *self = GTK_WINDOW (root);
GtkWindowPrivate *priv = gtk_window_get_instance_private (self);
return priv->constraint_solver;
}
static void
gtk_window_native_get_surface_transform (GtkNative *native,
int *x,
@@ -2381,6 +2395,7 @@ static void
gtk_window_root_interface_init (GtkRootInterface *iface)
{
iface->get_display = gtk_window_root_get_display;
iface->get_constraint_solver = gtk_window_root_get_constraint_solver;
}
static void
@@ -4723,6 +4738,7 @@ gtk_window_finalize (GObject *object)
priv->mnemonics_display_timeout_id = 0;
}
g_clear_object (&priv->constraint_solver);
g_clear_object (&priv->renderer);
G_OBJECT_CLASS (gtk_window_parent_class)->finalize (object);

View File

@@ -37,6 +37,9 @@ gtk_private_sources = files([
'gtkcolorpickershell.c',
'gtkcolorscale.c',
'gtkcolorswatch.c',
'gtkconstraintexpression.c',
'gtkconstraintsolver.c',
'gtkconstraintvflparser.c',
'gtkcssanimatedstyle.c',
'gtkcssanimation.c',
'gtkcssarrayvalue.c',
@@ -200,6 +203,8 @@ gtk_public_sources = files([
'gtkcombobox.c',
'gtkcomboboxtext.c',
'gtkcomposetable.c',
'gtkconstraintlayout.c',
'gtkconstraint.c',
'gtkcontainer.c',
'gtkcssprovider.c',
'gtkdialog.c',
@@ -459,6 +464,8 @@ gtk_public_headers = files([
'gtkcolorutils.h',
'gtkcombobox.h',
'gtkcomboboxtext.h',
'gtkconstraintlayout.h',
'gtkconstraint.h',
'gtkcontainer.h',
'gtkcssprovider.h',
'gtkcustomlayout.h',

View File

@@ -0,0 +1,368 @@
#include <locale.h>
#include "../../gtk/gtkconstrainttypesprivate.h"
#include "../../gtk/gtkconstraintsolverprivate.h"
#include "../../gtk/gtkconstraintexpressionprivate.h"
static void
constraint_solver_simple (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 167.0);
GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 2.0);
GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (y);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
double y_value = gtk_constraint_variable_get_value (y);
g_assert_cmpfloat_with_epsilon (x_value, y_value, 0.001);
g_assert_cmpfloat_with_epsilon (x_value, 0.0, 0.001);
g_assert_cmpfloat_with_epsilon (y_value, 0.0, 0.001);
gtk_constraint_variable_unref (y);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_stay (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 5.0);
GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 10.0);
gtk_constraint_solver_add_stay_variable (solver, x, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_stay_variable (solver, y, GTK_CONSTRAINT_WEIGHT_WEAK);
double x_value = gtk_constraint_variable_get_value (x);
double y_value = gtk_constraint_variable_get_value (y);
g_assert_cmpfloat_with_epsilon (x_value, 5.0, 0.001);
g_assert_cmpfloat_with_epsilon (y_value, 10.0, 0.001);
gtk_constraint_variable_unref (x);
gtk_constraint_variable_unref (y);
g_object_unref (solver);
}
static void
constraint_solver_variable_geq_constant (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
GtkConstraintExpression *e = gtk_constraint_expression_new (100.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_GE, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
g_assert_cmpfloat (x_value, >=, 100.0);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_variable_leq_constant (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 100.0);
GtkConstraintExpression *e = gtk_constraint_expression_new (10.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_LE, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
g_assert_cmpfloat (x_value, <=, 10.0);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_variable_eq_constant (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
GtkConstraintExpression *e = gtk_constraint_expression_new (100.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
g_assert_cmpfloat_with_epsilon (x_value, 100.0, 0.001);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_eq_with_stay (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 10.0);
GtkConstraintVariable *width = gtk_constraint_solver_create_variable (solver, NULL, "width", 10.0);
GtkConstraintVariable *right_min = gtk_constraint_solver_create_variable (solver, NULL, "rightMin", 100.0);
GtkConstraintExpressionBuilder builder;
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, x);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_term (&builder, width);
GtkConstraintExpression *right = gtk_constraint_expression_builder_finish (&builder);
gtk_constraint_solver_add_stay_variable (solver, width, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_stay_variable (solver, right_min, GTK_CONSTRAINT_WEIGHT_WEAK);
gtk_constraint_solver_add_constraint (solver,
right_min, GTK_CONSTRAINT_RELATION_EQ, right,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
double x_value = gtk_constraint_variable_get_value (x);
double width_value = gtk_constraint_variable_get_value (width);
g_assert_cmpfloat_with_epsilon (x_value, 90.0, 0.001);
g_assert_cmpfloat_with_epsilon (width_value, 10.0, 0.001);
gtk_constraint_variable_unref (right_min);
gtk_constraint_variable_unref (width);
gtk_constraint_variable_unref (x);
g_object_unref (solver);
}
static void
constraint_solver_cassowary (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, "x", 0.0);
GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, "y", 0.0);
GtkConstraintExpression *e;
e = gtk_constraint_expression_new_from_variable (y);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_LE, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
e = gtk_constraint_expression_plus_constant (gtk_constraint_expression_new_from_variable (x), 3.0);
gtk_constraint_solver_add_constraint (solver,
y, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
e = gtk_constraint_expression_new (10.0);
gtk_constraint_solver_add_constraint (solver,
x, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_WEAK);
e = gtk_constraint_expression_new (10.0);
gtk_constraint_solver_add_constraint (solver,
y, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_WEAK);
double x_val = gtk_constraint_variable_get_value (x);
double y_val = gtk_constraint_variable_get_value (y);
g_test_message ("x = %g, y = %g", x_val, y_val);
/* The system is unstable and has two possible solutions we need to test */
g_assert_true ((G_APPROX_VALUE (x_val, 10.0, 1e-8) &&
G_APPROX_VALUE (y_val, 13.0, 1e-8)) ||
(G_APPROX_VALUE (x_val, 7.0, 1e-8) &&
G_APPROX_VALUE (y_val, 10.0, 1e-8)));
gtk_constraint_variable_unref (x);
gtk_constraint_variable_unref (y);
g_object_unref (solver);
}
static void
constraint_solver_edit_var_required (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, "a", 0.0);
gtk_constraint_solver_add_stay_variable (solver, a, GTK_CONSTRAINT_WEIGHT_STRONG);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001);
gtk_constraint_solver_add_edit_variable (solver, a, GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_begin_edit (solver);
gtk_constraint_solver_suggest_value (solver, a, 2.0);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001);
gtk_constraint_solver_suggest_value (solver, a, 10.0);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001);
gtk_constraint_solver_end_edit (solver);
gtk_constraint_variable_unref (a);
g_object_unref (solver);
}
static void
constraint_solver_edit_var_suggest (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, "a", 0.0);
GtkConstraintVariable *b = gtk_constraint_solver_create_variable (solver, NULL, "b", 0.0);
gtk_constraint_solver_add_stay_variable (solver, a, GTK_CONSTRAINT_WEIGHT_STRONG);
GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (b);
gtk_constraint_solver_add_constraint (solver,
a, GTK_CONSTRAINT_RELATION_EQ, e,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_resolve (solver);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 0.0, 0.001);
gtk_constraint_solver_add_edit_variable (solver, a, GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_solver_begin_edit (solver);
gtk_constraint_solver_suggest_value (solver, a, 2.0);
gtk_constraint_solver_resolve (solver);
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_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_variable_unref (a);
gtk_constraint_variable_unref (b);
g_object_unref (solver);
}
static void
constraint_solver_paper (void)
{
GtkConstraintSolver *solver = gtk_constraint_solver_new ();
GtkConstraintVariable *left = gtk_constraint_solver_create_variable (solver, NULL, "left", 0.0);
GtkConstraintVariable *middle = gtk_constraint_solver_create_variable (solver, NULL, "middle", 0.0);
GtkConstraintVariable *right = gtk_constraint_solver_create_variable (solver, NULL, "right", 0.0);
GtkConstraintExpressionBuilder builder;
GtkConstraintExpression *expr;
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, left);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_term (&builder, right);
gtk_constraint_expression_builder_divide_by (&builder);
gtk_constraint_expression_builder_constant (&builder, 2.0);
expr = gtk_constraint_expression_builder_finish (&builder);
gtk_constraint_solver_add_constraint (solver,
middle, GTK_CONSTRAINT_RELATION_EQ, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
gtk_constraint_expression_builder_init (&builder, solver);
gtk_constraint_expression_builder_term (&builder, left);
gtk_constraint_expression_builder_plus (&builder);
gtk_constraint_expression_builder_constant (&builder, 10.0);
expr = gtk_constraint_expression_builder_finish (&builder);
gtk_constraint_solver_add_constraint (solver,
right, GTK_CONSTRAINT_RELATION_EQ, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
expr = gtk_constraint_expression_new (100.0);
gtk_constraint_solver_add_constraint (solver,
right, GTK_CONSTRAINT_RELATION_LE, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
expr = gtk_constraint_expression_new (0.0);
gtk_constraint_solver_add_constraint (solver,
left, GTK_CONSTRAINT_RELATION_GE, expr,
GTK_CONSTRAINT_WEIGHT_REQUIRED);
g_test_message ("Check constraints hold");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle),
(gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0,
0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right),
gtk_constraint_variable_get_value (left) + 10.0,
0.001);
g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0);
g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0);
gtk_constraint_variable_set_value (middle, 45.0);
gtk_constraint_solver_add_stay_variable (solver, middle, GTK_CONSTRAINT_WEIGHT_WEAK);
g_test_message ("Check constraints hold after setting middle");
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle),
(gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0,
0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right),
gtk_constraint_variable_get_value (left) + 10.0,
0.001);
g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0);
g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (left), 40.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle), 45.0, 0.001);
g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right), 50.0, 0.001);
gtk_constraint_variable_unref (left);
gtk_constraint_variable_unref (middle);
gtk_constraint_variable_unref (right);
g_object_unref (solver);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
setlocale (LC_ALL, "C");
g_test_add_func ("/constraint-solver/paper", constraint_solver_paper);
g_test_add_func ("/constraint-solver/simple", constraint_solver_simple);
g_test_add_func ("/constraint-solver/constant/eq", constraint_solver_variable_eq_constant);
g_test_add_func ("/constraint-solver/constant/ge", constraint_solver_variable_geq_constant);
g_test_add_func ("/constraint-solver/constant/le", constraint_solver_variable_leq_constant);
g_test_add_func ("/constraint-solver/stay/simple", constraint_solver_stay);
g_test_add_func ("/constraint-solver/stay/eq", constraint_solver_eq_with_stay);
g_test_add_func ("/constraint-solver/cassowary", constraint_solver_cassowary);
g_test_add_func ("/constraint-solver/edit/required", constraint_solver_edit_var_required);
g_test_add_func ("/constraint-solver/edit/suggest", constraint_solver_edit_var_suggest);
return g_test_run ();
}

View File

@@ -17,6 +17,11 @@ tests = [
['builderparser'],
['cellarea'],
['check-icon-names'],
['constraint-solver', [
'../../gtk/gtkconstraintsolver.c',
'../../gtk/gtkconstraintexpression.c',
], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']
],
['cssprovider'],
['rbtree-crash', ['../../gtk/gtkrbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
['defaultvalue'],