diff --git a/demos/constraint-editor/constraint-editor-application.c b/demos/constraint-editor/constraint-editor-application.c index 09b5d64270..aa130d9c13 100644 --- a/demos/constraint-editor/constraint-editor-application.c +++ b/demos/constraint-editor/constraint-editor-application.c @@ -51,6 +51,7 @@ static void constraint_editor_application_startup (GApplication *app) { const char *quit_accels[2] = { "Q", NULL }; + const char *open_accels[2] = { "O", NULL }; GtkCssProvider *provider; G_APPLICATION_CLASS (constraint_editor_application_parent_class)->startup (app); @@ -59,6 +60,7 @@ constraint_editor_application_startup (GApplication *app) app_entries, G_N_ELEMENTS (app_entries), app); gtk_application_set_accels_for_action (GTK_APPLICATION (app), "app.quit", quit_accels); + gtk_application_set_accels_for_action (GTK_APPLICATION (app), "win.open", open_accels); provider = gtk_css_provider_new (); gtk_css_provider_load_from_resource (provider, "/org/gtk/gtk4/constraint-editor/constraint-editor.css"); @@ -76,6 +78,23 @@ constraint_editor_application_activate (GApplication *app) gtk_window_present (GTK_WINDOW (win)); } +static void +constraint_editor_application_open (GApplication *app, + GFile **files, + gint n_files, + const gchar *hint) +{ + ConstraintEditorWindow *win; + gint i; + + for (i = 0; i < n_files; i++) + { + win = constraint_editor_window_new (CONSTRAINT_EDITOR_APPLICATION (app)); + constraint_editor_window_load (win, files[i]); + gtk_window_present (GTK_WINDOW (win)); + } +} + static void constraint_editor_application_class_init (ConstraintEditorApplicationClass *class) { @@ -83,6 +102,7 @@ constraint_editor_application_class_init (ConstraintEditorApplicationClass *clas application_class->startup = constraint_editor_application_startup; application_class->activate = constraint_editor_application_activate; + application_class->open = constraint_editor_application_open; } ConstraintEditorApplication * @@ -90,5 +110,6 @@ constraint_editor_application_new (void) { return g_object_new (CONSTRAINT_EDITOR_APPLICATION_TYPE, "application-id", "org.gtk.gtk4.ConstraintEditor", + "flags", G_APPLICATION_HANDLES_OPEN, NULL); } diff --git a/demos/constraint-editor/constraint-editor-window.c b/demos/constraint-editor/constraint-editor-window.c index a8fa33464d..823deb3c96 100644 --- a/demos/constraint-editor/constraint-editor-window.c +++ b/demos/constraint-editor/constraint-editor-window.c @@ -35,36 +35,307 @@ struct _ConstraintEditorWindow G_DEFINE_TYPE(ConstraintEditorWindow, constraint_editor_window, GTK_TYPE_APPLICATION_WINDOW); +static GtkConstraintTarget * +find_target (GListModel *model, + GtkConstraintTarget *orig) +{ + const char *name; + const char *model_name; + gpointer item; + int i; + + if (orig == NULL) + return NULL; + + if (GTK_IS_LABEL (orig)) + name = gtk_label_get_label (GTK_LABEL (orig)); + else if (GTK_IS_CONSTRAINT_GUIDE (orig)) + name = gtk_constraint_guide_get_name (GTK_CONSTRAINT_GUIDE (orig)); + else + { + g_warning ("Don't know how to handle %s targets", G_OBJECT_TYPE_NAME (orig)); + return NULL; + } + for (i = 0; i < g_list_model_get_n_items (model); i++) + { + item = g_list_model_get_item (model, i); + g_object_unref (item); + if (GTK_IS_WIDGET (item)) + model_name = gtk_widget_get_name (GTK_WIDGET (item)); + else + model_name = gtk_constraint_guide_get_name (GTK_CONSTRAINT_GUIDE (item)); + + if (strcmp (name, model_name) == 0) + return GTK_CONSTRAINT_TARGET (item); + } + g_warning ("Failed to find target '%s'", name); + + return NULL; +} + gboolean constraint_editor_window_load (ConstraintEditorWindow *self, GFile *file) { - GBytes *bytes; + char *path; + GtkBuilder *builder; + GError *error = NULL; + GtkWidget *view; + GtkLayoutManager *layout; + GtkWidget *child; + const char *name; + gpointer item; + int i; + GListModel *list; - bytes = g_file_load_bytes (file, NULL, NULL, NULL); - if (bytes == NULL) - return FALSE; + path = g_file_get_path (file); - if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL)) + builder = gtk_builder_new (); + if (!gtk_builder_add_from_file (builder, path, &error)) { - g_bytes_unref (bytes); + g_print ("Could not load %s: %s", path, error->message); + g_error_free (error); + g_free (path); + g_object_unref (builder); return FALSE; } -#if 0 + view = GTK_WIDGET (gtk_builder_get_object (builder, "view")); + if (!GTK_IS_BOX (view)) + { + g_print ("Could not load %s: No GtkBox named 'view'", path); + g_free (path); + g_object_unref (builder); + return FALSE; + } + layout = gtk_widget_get_layout_manager (view); + if (!GTK_IS_CONSTRAINT_LAYOUT (layout)) + { + g_print ("Could not load %s: Widget 'view' does not use GtkConstraintLayout", path); + g_free (path); + g_object_unref (builder); + return FALSE; + } - 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 + for (child = gtk_widget_get_first_child (view); + child; + child = gtk_widget_get_next_sibling (child)) + { + if (!GTK_IS_LABEL (child)) + { + g_print ("Skipping non-GtkLabel child\n"); + continue; + } - g_bytes_unref (bytes); + name = gtk_label_get_label (GTK_LABEL (child)); + constraint_view_add_child (CONSTRAINT_VIEW (self->view), name); + } + + list = gtk_constraint_layout_observe_guides (GTK_CONSTRAINT_LAYOUT (layout)); + for (i = 0; i < g_list_model_get_n_items (list); i++) + { + GtkConstraintGuide *guide, *clone; + int w, h; + + item = g_list_model_get_item (list, i); + guide = GTK_CONSTRAINT_GUIDE (item); + + /* need to clone here, to attach to the right targets */ + clone = gtk_constraint_guide_new (); + gtk_constraint_guide_set_name (clone, gtk_constraint_guide_get_name (guide)); + gtk_constraint_guide_set_strength (clone, gtk_constraint_guide_get_strength (guide)); + gtk_constraint_guide_get_min_size (guide, &w, &h); + gtk_constraint_guide_set_min_size (clone, w, h); + gtk_constraint_guide_get_nat_size (guide, &w, &h); + gtk_constraint_guide_set_nat_size (clone, w, h); + gtk_constraint_guide_get_max_size (guide, &w, &h); + gtk_constraint_guide_set_max_size (clone, w, h); + constraint_view_add_guide (CONSTRAINT_VIEW (self->view), clone); + g_object_unref (guide); + g_object_unref (clone); + } + g_object_unref (list); + + list = gtk_constraint_layout_observe_constraints (GTK_CONSTRAINT_LAYOUT (layout)); + for (i = 0; i < g_list_model_get_n_items (list); i++) + { + GtkConstraint *constraint; + GtkConstraint *clone; + GtkConstraintTarget *target; + GtkConstraintTarget *source; + + item = g_list_model_get_item (list, i); + constraint = GTK_CONSTRAINT (item); + + target = gtk_constraint_get_target (constraint); + source = gtk_constraint_get_source (constraint); + clone = gtk_constraint_new (find_target (constraint_view_get_model (CONSTRAINT_VIEW (self->view)), target), + gtk_constraint_get_target_attribute (constraint), + gtk_constraint_get_relation (constraint), + find_target (constraint_view_get_model (CONSTRAINT_VIEW (self->view)), source), + gtk_constraint_get_target_attribute (constraint), + gtk_constraint_get_multiplier (constraint), + gtk_constraint_get_constant (constraint), + gtk_constraint_get_strength (constraint)); + + constraint_view_add_constraint (CONSTRAINT_VIEW (self->view), clone); + + g_object_unref (constraint); + g_object_unref (clone); + } + g_object_unref (list); + + g_free (path); + g_object_unref (builder); return TRUE; } +static void +open_response_cb (GtkNativeDialog *dialog, + gint response, + ConstraintEditorWindow *self) +{ + gtk_native_dialog_hide (dialog); + + if (response == GTK_RESPONSE_ACCEPT) + { + GFile *file; + + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + constraint_editor_window_load (self, file); + g_object_unref (file); + } + + gtk_native_dialog_destroy (dialog); +} + +static void +open_cb (GtkWidget *button, + ConstraintEditorWindow *self) +{ + GtkFileChooserNative *dialog; + + dialog = gtk_file_chooser_native_new ("Open file", + GTK_WINDOW (self), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Load", + "_Cancel"); + + gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (dialog), TRUE); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), "."); + g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), self); + gtk_native_dialog_show (GTK_NATIVE_DIALOG (dialog)); +} + +static void +serialize_child (GString *str, + int indent, + GtkWidget *child) +{ + const char *name; + + name = gtk_widget_get_name (child); + g_string_append_printf (str, "%*s\n", indent, ""); + g_string_append_printf (str, "%*s \n", indent, "", name); + g_string_append_printf (str, "%*s %s\n", indent, "", name); + g_string_append_printf (str, "%*s \n", indent, ""); + g_string_append_printf (str, "%*s\n", indent, ""); +} + +static char * +serialize_model (GListModel *list) +{ + GString *str = g_string_new (""); + int i; + + g_string_append (str, "\n"); + g_string_append (str, " \n"); + g_string_append (str, " \n"); + g_string_append (str, " \n"); + g_string_append (str, " \n"); + for (i = 0; i < g_list_model_get_n_items (list); i++) + { + gpointer item = g_list_model_get_item (list, i); + g_object_unref (item); + if (GTK_IS_CONSTRAINT (item)) + constraint_editor_serialize_constraint (str, 10, GTK_CONSTRAINT (item)); + else if (GTK_IS_CONSTRAINT_GUIDE (item)) + guide_editor_serialize_guide (str, 10, GTK_CONSTRAINT_GUIDE (item)); + } + g_string_append (str, " \n"); + g_string_append (str, " \n"); + g_string_append (str, " \n"); + for (i = 0; i < g_list_model_get_n_items (list); i++) + { + gpointer item = g_list_model_get_item (list, i); + g_object_unref (item); + if (GTK_IS_WIDGET (item)) + serialize_child (str, 4, GTK_WIDGET (item)); + } + g_string_append (str, " \n"); + g_string_append (str, "\n"); + + return g_string_free (str, FALSE); +} + + +static void +save_response_cb (GtkNativeDialog *dialog, + gint response, + ConstraintEditorWindow *self) +{ + gtk_native_dialog_hide (dialog); + + if (response == GTK_RESPONSE_ACCEPT) + { + GListModel *model; + char *text, *filename; + GError *error = NULL; + + model = constraint_view_get_model (CONSTRAINT_VIEW (self->view)); + text = serialize_model (model); + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!g_file_set_contents (filename, text, -1, &error)) + { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))), + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "Saving failed"); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); + g_error_free (error); + } + g_free (filename); + } + + gtk_native_dialog_destroy (dialog); +} + +static void +save_cb (GtkWidget *button, + ConstraintEditorWindow *self) +{ + GtkFileChooserNative *dialog; + + dialog = gtk_file_chooser_native_new ("Save constraints", + GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (button))), + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Save", + "_Cancel"); + + gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (dialog), TRUE); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), "."); + g_signal_connect (dialog, "response", G_CALLBACK (save_response_cb), self); + gtk_native_dialog_show (GTK_NATIVE_DIALOG (dialog)); +} + static void constraint_editor_window_finalize (GObject *object) { @@ -95,8 +366,9 @@ add_guide (ConstraintEditorWindow *win) 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); + guide = gtk_constraint_guide_new (); + gtk_constraint_guide_set_name (guide, name); + g_free (name); constraint_view_add_guide (CONSTRAINT_VIEW (win->view), guide); } @@ -158,7 +430,6 @@ 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)); } @@ -213,6 +484,8 @@ constraint_editor_window_class_init (ConstraintEditorWindowClass *class) 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, open_cb); + gtk_widget_class_bind_template_callback (widget_class, save_cb); 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); @@ -294,17 +567,27 @@ create_widget_func (gpointer item, { ConstraintEditorWindow *win = user_data; const char *name; + char *freeme = NULL; GtkWidget *row, *box, *label, *button; - name = (const char *)g_object_get_data (G_OBJECT (item), "name"); + if (GTK_IS_WIDGET (item)) + name = gtk_widget_get_name (GTK_WIDGET (item)); + else if (GTK_IS_CONSTRAINT_GUIDE (item)) + name = gtk_constraint_guide_get_name (GTK_CONSTRAINT_GUIDE (item)); + else if (GTK_IS_CONSTRAINT (item)) + name = freeme = constraint_editor_constraint_to_string (GTK_CONSTRAINT (item)); + else + name = ""; row = gtk_list_box_row_new (); - g_object_set_data (G_OBJECT (row), "item", item); + g_object_set_data_full (G_OBJECT (row), "item", g_object_ref (item), g_object_unref); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); label = gtk_label_new (name); - g_object_set (label, - "margin", 10, - NULL); + if (GTK_IS_WIDGET (item) || GTK_IS_CONSTRAINT_GUIDE (item)) + g_object_bind_property (item, "name", + label, "label", + G_BINDING_DEFAULT); + 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); @@ -330,6 +613,8 @@ create_widget_func (gpointer item, gtk_container_add (GTK_CONTAINER (box), button); } + g_free (freeme); + return row; } diff --git a/demos/constraint-editor/constraint-editor-window.ui b/demos/constraint-editor/constraint-editor-window.ui index c2c9a009ec..0d2e72fcea 100644 --- a/demos/constraint-editor/constraint-editor-window.ui +++ b/demos/constraint-editor/constraint-editor-window.ui @@ -11,6 +11,20 @@ GTK Constraint Editor 1 + + + document-open-symbolic + Open ui file + + + + + + document-save-symbolic + Save to ui file + + + diff --git a/demos/constraint-editor/constraint-editor.c b/demos/constraint-editor/constraint-editor.c index 5a85b35ecf..62af6e356f 100644 --- a/demos/constraint-editor/constraint-editor.c +++ b/demos/constraint-editor/constraint-editor.c @@ -26,7 +26,6 @@ struct _ConstraintEditor GtkWidget parent_instance; GtkWidget *grid; - GtkWidget *name; GtkWidget *target; GtkWidget *target_attr; GtkWidget *relation; @@ -66,8 +65,12 @@ get_target_name (GtkConstraintTarget *target) { if (target == NULL) return "super"; + else if (GTK_IS_WIDGET (target)) + return gtk_widget_get_name (GTK_WIDGET (target)); + else if (GTK_IS_CONSTRAINT_GUIDE (target)) + return gtk_constraint_guide_get_name (GTK_CONSTRAINT_GUIDE (target)); else - return (const char *)g_object_get_data (G_OBJECT (target), "name"); + return ""; } static void @@ -145,13 +148,19 @@ get_target (GListModel *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; + g_object_unref (item); 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; + else if (GTK_IS_WIDGET (item)) + { + if (strcmp (id, gtk_widget_get_name (GTK_WIDGET (item))) == 0) + return item; + } + else if (GTK_IS_CONSTRAINT_GUIDE (item)) + { + if (strcmp (id, gtk_constraint_guide_get_name (GTK_CONSTRAINT_GUIDE (item))) == 0) + return item; + } } return NULL; @@ -226,6 +235,40 @@ get_strength_nick (GtkConstraintStrength strength) return nick; } +void +constraint_editor_serialize_constraint (GString *str, + int indent, + GtkConstraint *constraint) +{ + const char *target; + const char *target_attr; + const char *relation; + const char *source; + const char *source_attr; + double multiplier; + double constant; + const char *strength; + + target = get_target_name (gtk_constraint_get_target (constraint)); + target_attr = get_attr_nick (gtk_constraint_get_target_attribute (constraint)); + relation = get_relation_nick (gtk_constraint_get_relation (constraint)); + source = get_target_name (gtk_constraint_get_source (constraint)); + source_attr = get_attr_nick (gtk_constraint_get_source_attribute (constraint)); + multiplier = gtk_constraint_get_multiplier (constraint); + constant = gtk_constraint_get_constant (constraint); + strength = get_strength_nick (gtk_constraint_get_strength (constraint)); + + g_string_append_printf (str, "%*s\n", indent, "", strength); +} + static void create_constraint (GtkButton *button, ConstraintEditor *editor) @@ -240,7 +283,6 @@ create_constraint (GtkButton *button, 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); @@ -262,15 +304,12 @@ create_constraint (GtkButton *button, 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); } @@ -295,6 +334,53 @@ source_attr_changed (ConstraintEditor *editor) } } +char * +constraint_editor_constraint_to_string (GtkConstraint *constraint) +{ + GString *str; + const char *name; + const char *attr; + const char *relation; + double c, m; + + str = g_string_new (""); + + name = get_target_name (gtk_constraint_get_target (constraint)); + attr = get_attr_nick (gtk_constraint_get_target_attribute (constraint)); + relation = get_relation_nick (gtk_constraint_get_relation (constraint)); + + if (name == NULL) + name = "[ ]"; + + g_string_append_printf (str, "%s.%s %s ", name, attr, relation); + + c = gtk_constraint_get_constant (constraint); + + attr = get_attr_nick (gtk_constraint_get_source_attribute (constraint)); + if (strcmp (attr, "none") != 0) + { + name = get_target_name (gtk_constraint_get_source (constraint)); + m = gtk_constraint_get_multiplier (constraint); + + 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); + + return g_string_free (str, FALSE); +} + static void update_preview (ConstraintEditor *editor) { @@ -368,8 +454,6 @@ constraint_editor_init (ConstraintEditor *editor) gtk_widget_init_template (GTK_WIDGET (editor)); } -static int constraint_counter; - static void constraint_editor_constructed (GObject *object) { @@ -394,9 +478,6 @@ constraint_editor_constructed (GObject *object) 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); @@ -435,13 +516,6 @@ constraint_editor_constructed (GObject *object) } 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"); @@ -554,7 +628,6 @@ constraint_editor_class_init (ConstraintEditorClass *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); diff --git a/demos/constraint-editor/constraint-editor.h b/demos/constraint-editor/constraint-editor.h index c5940e254b..1b6125bfad 100644 --- a/demos/constraint-editor/constraint-editor.h +++ b/demos/constraint-editor/constraint-editor.h @@ -27,3 +27,8 @@ G_DECLARE_FINAL_TYPE (ConstraintEditor, constraint_editor, CONSTRAINT, EDITOR, G ConstraintEditor * constraint_editor_new (GListModel *model, GtkConstraint *constraint); + +void constraint_editor_serialize_constraint (GString *str, + int indent, + GtkConstraint *constraint); +char *constraint_editor_constraint_to_string (GtkConstraint *constraint); diff --git a/demos/constraint-editor/constraint-editor.ui b/demos/constraint-editor/constraint-editor.ui index 604757659a..8d12a37758 100644 --- a/demos/constraint-editor/constraint-editor.ui +++ b/demos/constraint-editor/constraint-editor.ui @@ -6,23 +6,6 @@ 20 10 10 - - - Name - - 0 - 0 - - - - - - - 1 - 0 - - - Target diff --git a/demos/constraint-editor/constraint-view.c b/demos/constraint-editor/constraint-view.c index 7a4b094df5..982338c93d 100644 --- a/demos/constraint-editor/constraint-view.c +++ b/demos/constraint-editor/constraint-view.c @@ -21,7 +21,7 @@ struct _ConstraintView { GtkWidget parent; - GListStore *store; + GListModel *model; GtkWidget *drag_widget; }; @@ -37,7 +37,7 @@ constraint_view_dispose (GObject *object) while ((child = gtk_widget_get_first_child (GTK_WIDGET (view))) != NULL) gtk_widget_unparent (child); - g_clear_object (&view->store); + g_clear_object (&view->model); G_OBJECT_CLASS (constraint_view_parent_class)->dispose (object); } @@ -70,30 +70,38 @@ update_weak_position (ConstraintView *self, constraint); g_object_set_data (G_OBJECT (child), "x-constraint", NULL); } - constraint = gtk_constraint_new_constant (child, - GTK_CONSTRAINT_ATTRIBUTE_CENTER_X, - GTK_CONSTRAINT_RELATION_EQ, - x, - GTK_CONSTRAINT_STRENGTH_WEAK); - gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager), - constraint); - g_object_set_data (G_OBJECT (child), "x-constraint", constraint); + if (x != -100) + { + constraint = gtk_constraint_new_constant (child, + GTK_CONSTRAINT_ATTRIBUTE_CENTER_X, + GTK_CONSTRAINT_RELATION_EQ, + x, + GTK_CONSTRAINT_STRENGTH_WEAK); + g_object_set_data (G_OBJECT (constraint), "internal", "yes"); + gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager), + constraint); + g_object_set_data (G_OBJECT (child), "x-constraint", constraint); + } constraint = (GtkConstraint *)g_object_get_data (G_OBJECT (child), "y-constraint"); if (constraint) { gtk_constraint_layout_remove_constraint (GTK_CONSTRAINT_LAYOUT (manager), - constraint); + constraint); g_object_set_data (G_OBJECT (child), "y-constraint", NULL); } - constraint = gtk_constraint_new_constant (child, - GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y, - GTK_CONSTRAINT_RELATION_EQ, - y, - GTK_CONSTRAINT_STRENGTH_WEAK); - gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager), - constraint); - g_object_set_data (G_OBJECT (child), "y-constraint", constraint); + if (y != -100) + { + constraint = gtk_constraint_new_constant (child, + GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y, + GTK_CONSTRAINT_RELATION_EQ, + y, + GTK_CONSTRAINT_STRENGTH_WEAK); + g_object_set_data (G_OBJECT (constraint), "internal", "yes"); + gtk_constraint_layout_add_constraint (GTK_CONSTRAINT_LAYOUT (manager), + constraint); + g_object_set_data (G_OBJECT (child), "y-constraint", constraint); + } } static void @@ -141,15 +149,48 @@ drag_end (GtkGestureDrag *drag, self->drag_widget = NULL; } +static gboolean +omit_internal (gpointer item, gpointer user_data) +{ + if (g_object_get_data (G_OBJECT (item), "internal")) + return FALSE; + + return TRUE; +} + static void constraint_view_init (ConstraintView *self) { + GtkLayoutManager *manager; GtkEventController *controller; + GListStore *list; + GListModel *all_children; + GListModel *all_constraints; + GListModel *guides; + GListModel *children; + GListModel *constraints; - gtk_widget_set_layout_manager (GTK_WIDGET (self), - gtk_constraint_layout_new ()); + manager = gtk_constraint_layout_new (); + gtk_widget_set_layout_manager (GTK_WIDGET (self), manager); + + all_children = gtk_widget_observe_children (GTK_WIDGET (self)); + all_constraints = gtk_constraint_layout_observe_constraints (GTK_CONSTRAINT_LAYOUT (manager)); + guides = gtk_constraint_layout_observe_guides (GTK_CONSTRAINT_LAYOUT (manager)); + constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, omit_internal, NULL, NULL); + children = (GListModel *)gtk_filter_list_model_new (all_children, omit_internal, NULL, NULL); + + list = g_list_store_new (G_TYPE_LIST_MODEL); + g_list_store_append (list, children); + g_list_store_append (list, guides); + g_list_store_append (list, constraints); + self->model = G_LIST_MODEL (gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (list))); + g_object_unref (children); + g_object_unref (guides); + g_object_unref (constraints); + g_object_unref (all_children); + g_object_unref (all_constraints); + g_object_unref (list); - self->store = g_list_store_new (G_TYPE_OBJECT); controller = (GtkEventController *)gtk_gesture_drag_new (); g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self); @@ -174,168 +215,103 @@ constraint_view_add_child (ConstraintView *view, 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_widget_set_name (frame, name); gtk_container_add (GTK_CONTAINER (frame), label); gtk_widget_set_parent (frame, GTK_WIDGET (view)); update_weak_position (view, frame, 100, 100); - - g_list_store_append (view->store, frame); } void constraint_view_remove_child (ConstraintView *view, GtkWidget *child) { - int i; - + update_weak_position (view, child, -100, -100); 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; + GtkConstraintLayout *layout; GtkWidget *frame; GtkWidget *label; const char *name; GtkConstraint *constraint; + struct { + const char *name; + GtkConstraintAttribute attr; + } names[] = { + { "left-constraint", GTK_CONSTRAINT_ATTRIBUTE_LEFT }, + { "top-constraint", GTK_CONSTRAINT_ATTRIBUTE_TOP }, + { "width-constraint", GTK_CONSTRAINT_ATTRIBUTE_WIDTH }, + { "height-constraint", GTK_CONSTRAINT_ATTRIBUTE_HEIGHT }, + }; + int i; - name = (const char *)g_object_get_data (G_OBJECT (guide), "name"); - + name = gtk_constraint_guide_get_name (guide); label = gtk_label_new (name); + g_object_bind_property (guide, "name", + label, "label", + G_BINDING_DEFAULT); + 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); + g_object_set_data (G_OBJECT (frame), "internal", "yes"); gtk_container_add (GTK_CONTAINER (frame), label); gtk_widget_insert_after (frame, GTK_WIDGET (view), NULL); 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)); + layout = GTK_CONSTRAINT_LAYOUT (gtk_widget_get_layout_manager (GTK_WIDGET (view))); + gtk_constraint_layout_add_guide (layout, 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); + for (i = 0; i < G_N_ELEMENTS (names); i++) + { + constraint = gtk_constraint_new (frame, + names[i].attr, + GTK_CONSTRAINT_RELATION_EQ, + guide, + names[i].attr, + 1.0, 0.0, + GTK_CONSTRAINT_STRENGTH_REQUIRED); + g_object_set_data (G_OBJECT (constraint), "internal", "yes"); + gtk_constraint_layout_add_constraint (layout, constraint); + g_object_set_data (G_OBJECT (guide), names[i].name, constraint); + } update_weak_position (view, frame, 150, 150); - - 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; + GtkConstraintLayout *layout; GtkWidget *frame; GtkConstraint *constraint; + const char *names[] = { + "left-constraint", + "top-constraint", + "width-constraint", + "height-constraint" + }; int i; - manager = gtk_widget_get_layout_manager (GTK_WIDGET (view)); + layout = GTK_CONSTRAINT_LAYOUT (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); + for (i = 0; i < G_N_ELEMENTS (names); i++) + { + constraint = (GtkConstraint*)g_object_get_data (G_OBJECT (guide), names[i]); + gtk_constraint_layout_remove_constraint (layout, constraint); + } frame = (GtkWidget *)g_object_get_data (G_OBJECT (guide), "frame"); + update_weak_position (view, frame, -100, -100); 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; - } - } + gtk_constraint_layout_remove_guide (layout, guide); } void @@ -347,8 +323,6 @@ constraint_view_add_constraint (ConstraintView *view, 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 @@ -356,23 +330,14 @@ 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); + return view->model; } diff --git a/demos/constraint-editor/guide-editor.c b/demos/constraint-editor/guide-editor.c index 39bd08a157..a7ea6b5da0 100644 --- a/demos/constraint-editor/guide-editor.c +++ b/demos/constraint-editor/guide-editor.c @@ -89,6 +89,29 @@ get_strength_nick (GtkConstraintStrength strength) return nick; } +void +guide_editor_serialize_guide (GString *str, + int indent, + GtkConstraintGuide *guide) +{ + int min_width, min_height; + int nat_width, nat_height; + int max_width, max_height; + const char *name; + const char *strength; + + gtk_constraint_guide_get_min_size (guide, &min_width, &min_height); + gtk_constraint_guide_get_nat_size (guide, &nat_width, &nat_height); + gtk_constraint_guide_get_max_size (guide, &max_width, &max_height); + name = gtk_constraint_guide_get_name (guide); + strength = get_strength_nick (gtk_constraint_guide_get_strength (guide)); + + g_string_append_printf (str, "%*s\n", indent, "", name, strength); +} + static void create_guide (GtkButton *button, GuideEditor *editor) @@ -105,7 +128,7 @@ create_guide (GtkButton *button, guide = gtk_constraint_guide_new (); name = gtk_editable_get_text (GTK_EDITABLE (editor->name)); - g_object_set_data_full (G_OBJECT (guide), "name", g_strdup (name), g_free); + gtk_constraint_guide_set_name (guide, name); 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)); @@ -238,8 +261,9 @@ guide_editor_constructed (GObject *object) 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); + nick = gtk_constraint_guide_get_name (editor->guide); + if (nick) + gtk_editable_set_text (GTK_EDITABLE (editor->name), nick); gtk_constraint_guide_get_min_size (editor->guide, &w, &h); gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_width), w); diff --git a/demos/constraint-editor/guide-editor.h b/demos/constraint-editor/guide-editor.h index d11cb4f3db..56ccbfd5d3 100644 --- a/demos/constraint-editor/guide-editor.h +++ b/demos/constraint-editor/guide-editor.h @@ -26,3 +26,7 @@ G_DECLARE_FINAL_TYPE (GuideEditor, guide_editor, GUIDE, EDITOR, GtkWidget) GuideEditor * guide_editor_new (GtkConstraintGuide *guide); + +void guide_editor_serialize_guide (GString *str, + int indent, + GtkConstraintGuide *guide); diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index ac9ea19fa9..6dc672ce8f 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -7338,6 +7338,10 @@ gtk_constraint_layout_remove_guide gtk_constraint_layout_add_constraints_from_description gtk_constraint_layout_add_constraints_from_descriptionv + +gtk_constraint_layout_observe_constraints +gtk_constraint_layout_observe_guides + GTK_TYPE_CONSTRAINT_LAYOUT gtk_constraint_layout_get_type diff --git a/gtk/gtkconstraintlayout.c b/gtk/gtkconstraintlayout.c index 4623798b5e..f23237e0d8 100644 --- a/gtk/gtkconstraintlayout.c +++ b/gtk/gtkconstraintlayout.c @@ -253,6 +253,9 @@ struct _GtkConstraintLayout /* HashSet */ GHashTable *guides; + + GListStore *constraints_observer; + GListStore *guides_observer; }; G_DEFINE_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK_TYPE_LAYOUT_CHILD) @@ -534,6 +537,19 @@ gtk_constraint_layout_finalize (GObject *gobject) { GtkConstraintLayout *self = GTK_CONSTRAINT_LAYOUT (gobject); + if (self->constraints_observer) + { + g_list_store_remove_all (self->constraints_observer); + g_object_remove_weak_pointer ((GObject *)self->constraints_observer, + (gpointer *)&self->constraints_observer); + } + if (self->guides_observer) + { + g_list_store_remove_all (self->guides_observer); + g_object_remove_weak_pointer ((GObject *)self->guides_observer, + (gpointer *)&self->guides_observer); + } + g_clear_pointer (&self->bound_attributes, g_hash_table_unref); g_clear_pointer (&self->constraints, g_hash_table_unref); g_clear_pointer (&self->guides, g_hash_table_unref); @@ -1709,10 +1725,32 @@ gtk_constraint_layout_add_constraint (GtkConstraintLayout *layout, layout_add_constraint (layout, constraint); g_hash_table_add (layout->constraints, constraint); + if (layout->constraints_observer) + g_list_store_append (layout->constraints_observer, constraint); gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout)); } +static void +list_store_remove_item (GListStore *store, + gpointer item) +{ + int n_items; + int i; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (store)); + for (i = 0; i < n_items; i++) + { + gpointer *model_item = g_list_model_get_item (G_LIST_MODEL (store), i); + g_object_unref (model_item); + if (item == model_item) + { + g_list_store_remove (store, i); + break; + } + } +} + /** * gtk_constraint_layout_remove_constraint: * @layout: a #GtkConstraintLayout @@ -1731,6 +1769,8 @@ gtk_constraint_layout_remove_constraint (GtkConstraintLayout *layout, gtk_constraint_detach (constraint); g_hash_table_remove (layout->constraints, constraint); + if (layout->constraints_observer) + list_store_remove_item (layout->constraints_observer, constraint); gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout)); } @@ -1757,6 +1797,8 @@ gtk_constraint_layout_remove_all_constraints (GtkConstraintLayout *layout) gtk_constraint_detach (constraint); g_hash_table_iter_remove (&iter); } + if (layout->constraints_observer) + g_list_store_remove_all (layout->constraints_observer); gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout)); } @@ -1783,6 +1825,8 @@ gtk_constraint_layout_add_guide (GtkConstraintLayout *layout, gtk_constraint_guide_set_layout (guide, layout); g_hash_table_add (layout->guides, guide); + if (layout->guides_observer) + g_list_store_append (layout->guides_observer, guide); gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout)); } @@ -1807,6 +1851,8 @@ gtk_constraint_layout_remove_guide (GtkConstraintLayout *layout, gtk_constraint_guide_set_layout (guide, NULL); g_hash_table_remove (layout->guides, guide); + if (layout->guides_observer) + list_store_remove_item (layout->guides_observer, guide); gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (layout)); } @@ -2026,6 +2072,8 @@ gtk_constraint_layout_add_constraints_from_descriptionv (GtkConstraintLayout *la layout_add_constraint (layout, constraint); g_hash_table_add (layout->constraints, constraint); + if (layout->constraints_observer) + g_list_store_append (layout->constraints_observer, constraint); res = g_list_prepend (res, constraint); } @@ -2116,3 +2164,83 @@ gtk_constraint_layout_add_constraints_from_description (GtkConstraintLayout *lay return res; } + +/** + * gtk_constraint_layout_observe_constraints: + * @layout: a #GtkConstraintLayout + * + * Returns a #GListModel to track the constraints that are + * part of @layout. + * + * Calling this function will enable extra internal bookkeeping + * to track constraints and emit signals on the returned listmodel. + * It may slow down operations a lot. + * + * Applications should try hard to avoid calling this function + * because of the slowdowns. + * + * Returns: (transfer full): a #GListModel tracking @layout's + * constraints + */ +GListModel * +gtk_constraint_layout_observe_constraints (GtkConstraintLayout *layout) +{ + GHashTableIter iter; + gpointer key; + + if (layout->constraints_observer) + return g_object_ref (G_LIST_MODEL (layout->constraints_observer)); + + layout->constraints_observer = g_list_store_new (GTK_TYPE_CONSTRAINT); + g_object_add_weak_pointer ((GObject *)layout->constraints_observer, + (gpointer *)&layout->constraints_observer); + + g_hash_table_iter_init (&iter, layout->constraints); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + GtkConstraint *constraint = key; + g_list_store_append (layout->constraints_observer, constraint); + } + + return G_LIST_MODEL (layout->constraints_observer); +} + +/** + * gtk_constraint_layout_observe_guides: + * @layout: a #GtkConstraintLayout + * + * Returns a #GListModel to track the guides that are + * part of @layout. + * + * Calling this function will enable extra internal bookkeeping + * to track guides and emit signals on the returned listmodel. + * It may slow down operations a lot. + * + * Applications should try hard to avoid calling this function + * because of the slowdowns. + * + * Returns: (transfer full): a #GListModel tracking @layout's + * guides + */ +GListModel * +gtk_constraint_layout_observe_guides (GtkConstraintLayout *layout) +{ + GHashTableIter iter; + gpointer key; + + if (layout->guides_observer) + return g_object_ref (G_LIST_MODEL (layout->guides_observer)); + + layout->guides_observer = g_list_store_new (GTK_TYPE_CONSTRAINT_GUIDE); + g_object_add_weak_pointer ((GObject *)layout->guides_observer, + (gpointer *)&layout->guides_observer); + + g_hash_table_iter_init (&iter, layout->guides); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + GtkConstraintGuide *guide = key; + g_list_store_append (layout->guides_observer, guide); + } + + return G_LIST_MODEL (layout->guides_observer); +} diff --git a/gtk/gtkconstraintlayout.h b/gtk/gtkconstraintlayout.h index 90d5e84926..fc7c6a468b 100644 --- a/gtk/gtkconstraintlayout.h +++ b/gtk/gtkconstraintlayout.h @@ -85,4 +85,9 @@ GList * gtk_constraint_layout_add_constraints_from_descriptionv GHashTable *views, GError **error); +GDK_AVAILABLE_IN_ALL +GListModel * gtk_constraint_layout_observe_constraints (GtkConstraintLayout *layout); +GDK_AVAILABLE_IN_ALL +GListModel * gtk_constraint_layout_observe_guides (GtkConstraintLayout *layout); + G_END_DECLS