1/*
2 * Copyright © 2019 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Matthias Clasen
18 */
19
20#include "config.h"
21
22#include "constraint-editor.h"
23
24struct _ConstraintEditor
25{
26 GtkWidget parent_instance;
27
28 GtkWidget *grid;
29 GtkWidget *target;
30 GtkWidget *target_attr;
31 GtkWidget *relation;
32 GtkWidget *source;
33 GtkWidget *source_attr;
34 GtkWidget *multiplier;
35 GtkWidget *constant;
36 GtkWidget *strength;
37 GtkWidget *preview;
38 GtkWidget *button;
39
40 GtkConstraint *constraint;
41 GListModel *model;
42
43 gboolean constructed;
44};
45
46enum {
47 PROP_MODEL = 1,
48 PROP_CONSTRAINT,
49 LAST_PROP
50};
51
52static GParamSpec *pspecs[LAST_PROP];
53
54enum {
55 DONE,
56 LAST_SIGNAL
57};
58
59static guint signals[LAST_SIGNAL];
60
61G_DEFINE_TYPE(ConstraintEditor, constraint_editor, GTK_TYPE_WIDGET);
62
63static const char *
64get_target_name (GtkConstraintTarget *target)
65{
66 if (target == NULL)
67 return "super";
68 else if (GTK_IS_WIDGET (target))
69 return gtk_widget_get_name (GTK_WIDGET (target));
70 else if (GTK_IS_CONSTRAINT_GUIDE (ptr: target))
71 return gtk_constraint_guide_get_name (guide: GTK_CONSTRAINT_GUIDE (ptr: target));
72 else
73 return "";
74}
75
76static void
77constraint_target_combo (GListModel *model,
78 GtkWidget *combo,
79 gboolean is_source)
80{
81 int i;
82
83 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "super", text: "Super");
84
85 if (model)
86 {
87 for (i = 0; i < g_list_model_get_n_items (list: model); i++)
88 {
89 GObject *item = g_list_model_get_object (list: model, position: i);
90 const char *name;
91
92 if (GTK_IS_CONSTRAINT (ptr: item))
93 continue;
94
95 name = get_target_name (target: GTK_CONSTRAINT_TARGET (ptr: item));
96
97 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: name, text: name);
98 g_object_unref (object: item);
99 }
100 }
101}
102
103static void
104constraint_attribute_combo (GtkWidget *combo,
105 gboolean is_source)
106{
107 if (is_source)
108 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "none", text: "None");
109 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "left", text: "Left");
110 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "right", text: "Right");
111 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "top", text: "Top");
112 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "bottom", text: "Bottom");
113 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "start", text: "Start");
114 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "end", text: "End");
115 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "width", text: "Width");
116 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "height", text: "Height");
117 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "center-x", text: "Center X");
118 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "center-y", text: "Center Y");
119 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "baseline", text: "Baseline");
120}
121
122static void
123constraint_relation_combo (GtkWidget *combo)
124{
125 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "le", text: "≤");
126 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "eq", text: "=");
127 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "ge", text: "≥");
128}
129
130static void
131constraint_strength_combo (GtkWidget *combo)
132{
133 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "weak", text: "Weak");
134 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "medium", text: "Medium");
135 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "strong", text: "Strong");
136 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "required", text: "Required");
137}
138
139static gpointer
140get_target (GListModel *model,
141 const char *id)
142{
143 int i;
144
145 if (id == NULL)
146 return NULL;
147
148 if (strcmp (s1: "super", s2: id) == 0)
149 return NULL;
150
151 for (i = 0; i < g_list_model_get_n_items (list: model); i++)
152 {
153 GObject *item = g_list_model_get_object (list: model, position: i);
154 g_object_unref (object: item);
155 if (GTK_IS_CONSTRAINT (ptr: item))
156 continue;
157 else if (GTK_IS_WIDGET (item))
158 {
159 if (strcmp (s1: id, s2: gtk_widget_get_name (GTK_WIDGET (item))) == 0)
160 return item;
161 }
162 else if (GTK_IS_CONSTRAINT_GUIDE (ptr: item))
163 {
164 if (strcmp (s1: id, s2: gtk_constraint_guide_get_name (guide: GTK_CONSTRAINT_GUIDE (ptr: item))) == 0)
165 return item;
166 }
167 }
168
169 return NULL;
170}
171
172static GtkConstraintAttribute
173get_target_attr (const char *id)
174{
175 GtkConstraintAttribute attr;
176 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_ATTRIBUTE);
177 GEnumValue *value = g_enum_get_value_by_nick (enum_class: class, nick: id);
178 attr = value->value;
179 g_type_class_unref (g_class: class);
180
181 return attr;
182}
183
184static const char *
185get_attr_nick (GtkConstraintAttribute attr)
186{
187 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_ATTRIBUTE);
188 GEnumValue *value = g_enum_get_value (enum_class: class, value: attr);
189 const char *nick = value->value_nick;
190 g_type_class_unref (g_class: class);
191
192 return nick;
193}
194
195static GtkConstraintRelation
196get_relation (const char *id)
197{
198 GtkConstraintRelation relation;
199 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_RELATION);
200 GEnumValue *value = g_enum_get_value_by_nick (enum_class: class, nick: id);
201 relation = value->value;
202 g_type_class_unref (g_class: class);
203
204 return relation;
205}
206
207static const char *
208get_relation_nick (GtkConstraintRelation relation)
209{
210 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_RELATION);
211 GEnumValue *value = g_enum_get_value (enum_class: class, value: relation);
212 const char *nick = value->value_nick;
213 g_type_class_unref (g_class: class);
214
215 return nick;
216}
217
218static const char *
219get_relation_display_name (GtkConstraintRelation relation)
220{
221 switch (relation)
222 {
223 case GTK_CONSTRAINT_RELATION_LE:
224 return "≤";
225 case GTK_CONSTRAINT_RELATION_EQ:
226 return "=";
227 case GTK_CONSTRAINT_RELATION_GE:
228 return "≥";
229 default:
230 return "?";
231 }
232}
233
234static GtkConstraintStrength
235get_strength (const char *id)
236{
237 GtkConstraintStrength strength;
238 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_STRENGTH);
239 GEnumValue *value = g_enum_get_value_by_nick (enum_class: class, nick: id);
240 strength = value->value;
241 g_type_class_unref (g_class: class);
242
243 return strength;
244}
245
246static const char *
247get_strength_nick (GtkConstraintStrength strength)
248{
249 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_STRENGTH);
250 GEnumValue *value = g_enum_get_value (enum_class: class, value: strength);
251 const char *nick = value->value_nick;
252 g_type_class_unref (g_class: class);
253
254 return nick;
255}
256
257void
258constraint_editor_serialize_constraint (GString *str,
259 int indent,
260 GtkConstraint *constraint)
261{
262 const char *target;
263 const char *target_attr;
264 const char *relation;
265 const char *source;
266 const char *source_attr;
267 double multiplier;
268 double constant;
269 const char *strength;
270
271 target = get_target_name (target: gtk_constraint_get_target (constraint));
272 target_attr = get_attr_nick (attr: gtk_constraint_get_target_attribute (constraint));
273 relation = get_relation_nick (relation: gtk_constraint_get_relation (constraint));
274 source = get_target_name (target: gtk_constraint_get_source (constraint));
275 source_attr = get_attr_nick (attr: gtk_constraint_get_source_attribute (constraint));
276 multiplier = gtk_constraint_get_multiplier (constraint);
277 constant = gtk_constraint_get_constant (constraint);
278 strength = get_strength_nick (strength: gtk_constraint_get_strength (constraint));
279
280 g_string_append_printf (string: str, format: "%*s<constraint target=\"%s\" target-attribute=\"%s\"\n", indent, "", target, target_attr);
281 g_string_append_printf (string: str, format: "%*s relation=\"%s\"\n", indent, "", relation);
282 if (strcmp (s1: source_attr, s2: "none") != 0)
283 {
284 g_string_append_printf (string: str, format: "%*s source=\"%s\" source-attribute=\"%s\"\n", indent, "", source, source_attr);
285 g_string_append_printf (string: str, format: "%*s multiplier=\"%g\"\n", indent, "", multiplier);
286 }
287 g_string_append_printf (string: str, format: "%*s constant=\"%g\"\n", indent, "", constant);
288 g_string_append_printf (string: str, format: "%*s strength=\"%s\" />\n", indent, "", strength);
289}
290
291static void
292create_constraint (GtkButton *button,
293 ConstraintEditor *editor)
294{
295 const char *id;
296 gpointer target;
297 GtkConstraintAttribute target_attr;
298 gpointer source;
299 GtkConstraintAttribute source_attr;
300 GtkConstraintRelation relation;
301 double multiplier;
302 double constant;
303 int strength;
304 GtkConstraint *constraint;
305
306 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target));
307 target = get_target (model: editor->model, id);
308 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target_attr));
309 target_attr = get_target_attr (id);
310
311 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source));
312 source = get_target (model: editor->model, id);
313 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
314 source_attr = get_target_attr (id);
315
316 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->relation));
317 relation = get_relation (id);
318
319 multiplier = g_ascii_strtod (nptr: gtk_editable_get_text (GTK_EDITABLE (editor->multiplier)), NULL);
320
321 constant = g_ascii_strtod (nptr: gtk_editable_get_text (GTK_EDITABLE (editor->constant)), NULL);
322
323 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->strength));
324 strength = get_strength (id);
325
326 constraint = gtk_constraint_new (target, target_attribute: target_attr,
327 relation,
328 source, source_attribute: source_attr,
329 multiplier,
330 constant,
331 strength);
332 g_signal_emit (instance: editor, signal_id: signals[DONE], detail: 0, constraint);
333 g_object_unref (object: constraint);
334}
335
336static void
337source_attr_changed (ConstraintEditor *editor)
338{
339 const char *id;
340
341 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
342 if (strcmp (s1: id, s2: "none") == 0)
343 {
344 gtk_combo_box_set_active (GTK_COMBO_BOX (editor->source), index_: -1);
345 gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), text: "");
346 gtk_widget_set_sensitive (widget: editor->source, FALSE);
347 gtk_widget_set_sensitive (widget: editor->multiplier, FALSE);
348 }
349 else
350 {
351 gtk_widget_set_sensitive (widget: editor->source, TRUE);
352 gtk_widget_set_sensitive (widget: editor->multiplier, TRUE);
353 gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), text: "1");
354 }
355}
356
357char *
358constraint_editor_constraint_to_string (GtkConstraint *constraint)
359{
360 GString *str;
361 const char *name;
362 const char *attr;
363 const char *relation;
364 double c, m;
365
366 str = g_string_new (init: "");
367
368 name = get_target_name (target: gtk_constraint_get_target (constraint));
369 attr = get_attr_nick (attr: gtk_constraint_get_target_attribute (constraint));
370 relation = get_relation_display_name (relation: gtk_constraint_get_relation (constraint));
371
372 if (name == NULL)
373 name = "[ ]";
374
375 g_string_append_printf (string: str, format: "%s.%s %s ", name, attr, relation);
376
377 c = gtk_constraint_get_constant (constraint);
378
379 attr = get_attr_nick (attr: gtk_constraint_get_source_attribute (constraint));
380 if (strcmp (s1: attr, s2: "none") != 0)
381 {
382 name = get_target_name (target: gtk_constraint_get_source (constraint));
383 m = gtk_constraint_get_multiplier (constraint);
384
385 if (name == NULL)
386 name = "[ ]";
387
388 g_string_append_printf (string: str, format: "%s.%s", name, attr);
389
390 if (m != 1.0)
391 g_string_append_printf (string: str, format: " × %g", m);
392
393 if (c > 0.0)
394 g_string_append_printf (string: str, format: " + %g", c);
395 else if (c < 0.0)
396 g_string_append_printf (string: str, format: " - %g", -c);
397 }
398 else
399 g_string_append_printf (string: str, format: "%g", c);
400
401 return g_string_free (string: str, FALSE);
402}
403
404static void
405update_preview (ConstraintEditor *editor)
406{
407 GString *str;
408 const char *name;
409 const char *attr;
410 char *relation;
411 const char *multiplier;
412 const char *constant;
413 double c, m;
414
415 if (!editor->constructed)
416 return;
417
418 str = g_string_new (init: "");
419
420 name = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target));
421 attr = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target_attr));
422 relation = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (editor->relation));
423
424 if (name == NULL)
425 name = "[ ]";
426
427 g_string_append_printf (string: str, format: "%s.%s %s ", name, attr, relation);
428 g_free (mem: relation);
429
430 constant = gtk_editable_get_text (GTK_EDITABLE (editor->constant));
431 c = g_ascii_strtod (nptr: constant, NULL);
432
433 attr = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
434 if (strcmp (s1: attr, s2: "none") != 0)
435 {
436 name = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source));
437 multiplier = gtk_editable_get_text (GTK_EDITABLE (editor->multiplier));
438 m = g_ascii_strtod (nptr: multiplier, NULL);
439
440 if (name == NULL)
441 name = "[ ]";
442
443 g_string_append_printf (string: str, format: "%s.%s", name, attr);
444
445 if (m != 1.0)
446 g_string_append_printf (string: str, format: " × %g", m);
447
448 if (c > 0.0)
449 g_string_append_printf (string: str, format: " + %g", c);
450 else if (c < 0.0)
451 g_string_append_printf (string: str, format: " - %g", -c);
452 }
453 else
454 g_string_append_printf (string: str, format: "%g", c);
455
456 gtk_label_set_label (GTK_LABEL (editor->preview), str: str->str);
457
458 g_string_free (string: str, TRUE);
459}
460
461static void
462update_button (ConstraintEditor *editor)
463{
464 const char *target = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->target));
465 const char *source = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source));
466 const char *source_attr = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->source_attr));
467
468 if (target &&
469 (source || (source_attr && get_target_attr (id: source_attr) == GTK_CONSTRAINT_ATTRIBUTE_NONE)))
470 gtk_widget_set_sensitive (widget: editor->button, TRUE);
471 else
472 gtk_widget_set_sensitive (widget: editor->button, FALSE);
473}
474
475static void
476constraint_editor_init (ConstraintEditor *editor)
477{
478 gtk_widget_init_template (GTK_WIDGET (editor));
479}
480
481static void
482constraint_editor_constructed (GObject *object)
483{
484 ConstraintEditor *editor = CONSTRAINT_EDITOR (ptr: object);
485
486 constraint_target_combo (model: editor->model, combo: editor->target, FALSE);
487 constraint_attribute_combo (combo: editor->target_attr, FALSE);
488 constraint_relation_combo (combo: editor->relation);
489 constraint_target_combo (model: editor->model, combo: editor->source, TRUE);
490 constraint_attribute_combo (combo: editor->source_attr, TRUE);
491
492 constraint_strength_combo (combo: editor->strength);
493
494 if (editor->constraint)
495 {
496 GtkConstraintTarget *target;
497 GtkConstraintAttribute attr;
498 GtkConstraintRelation relation;
499 GtkConstraintStrength strength;
500 const char *nick;
501 char *val;
502 double multiplier;
503 double constant;
504
505 target = gtk_constraint_get_target (constraint: editor->constraint);
506 nick = get_target_name (target);
507 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->target), active_id: nick);
508
509 attr = gtk_constraint_get_target_attribute (constraint: editor->constraint);
510 nick = get_attr_nick (attr);
511 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->target_attr), active_id: nick);
512
513 target = gtk_constraint_get_source (constraint: editor->constraint);
514 nick = get_target_name (target);
515 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->source), active_id: nick);
516
517 attr = gtk_constraint_get_source_attribute (constraint: editor->constraint);
518 nick = get_attr_nick (attr);
519 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->source_attr), active_id: nick);
520
521 relation = gtk_constraint_get_relation (constraint: editor->constraint);
522 nick = get_relation_nick (relation);
523 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->relation), active_id: nick);
524
525 multiplier = gtk_constraint_get_multiplier (constraint: editor->constraint);
526 val = g_strdup_printf (format: "%g", multiplier);
527 gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), text: val);
528 g_free (mem: val);
529
530 constant = gtk_constraint_get_constant (constraint: editor->constraint);
531 val = g_strdup_printf (format: "%g", constant);
532 gtk_editable_set_text (GTK_EDITABLE (editor->constant), text: val);
533 g_free (mem: val);
534
535 strength = gtk_constraint_get_strength (constraint: editor->constraint);
536 nick = get_strength_nick (strength);
537 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), active_id: nick);
538
539 gtk_button_set_label (GTK_BUTTON (editor->button), label: "Apply");
540 }
541 else
542 {
543 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->target_attr), active_id: "left");
544 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->source_attr), active_id: "left");
545 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->relation), active_id: "eq");
546 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), active_id: "required");
547
548 gtk_editable_set_text (GTK_EDITABLE (editor->multiplier), text: "1.0");
549 gtk_editable_set_text (GTK_EDITABLE (editor->constant), text: "0.0");
550
551 gtk_button_set_label (GTK_BUTTON (editor->button), label: "Create");
552 }
553
554 editor->constructed = TRUE;
555 update_preview (editor);
556 update_button (editor);
557}
558
559static void
560constraint_editor_set_property (GObject *object,
561 guint property_id,
562 const GValue *value,
563 GParamSpec *pspec)
564{
565 ConstraintEditor *self = CONSTRAINT_EDITOR (ptr: object);
566
567 switch (property_id)
568 {
569 case PROP_MODEL:
570 self->model = g_value_dup_object (value);
571 break;
572
573 case PROP_CONSTRAINT:
574 self->constraint = g_value_dup_object (value);
575 break;
576
577 default:
578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
579 }
580}
581
582static void
583constraint_editor_get_property (GObject *object,
584 guint property_id,
585 GValue *value,
586 GParamSpec *pspec)
587{
588 ConstraintEditor *self = CONSTRAINT_EDITOR (ptr: object);
589
590 switch (property_id)
591 {
592 case PROP_MODEL:
593 g_value_set_object (value, v_object: self->model);
594 break;
595
596 case PROP_CONSTRAINT:
597 g_value_set_object (value, v_object: self->constraint);
598 break;
599
600 default:
601 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
602 }
603}
604
605static void
606constraint_editor_dispose (GObject *object)
607{
608 ConstraintEditor *self = (ConstraintEditor *)object;
609
610 g_clear_pointer (&self->grid, gtk_widget_unparent);
611 g_clear_object (&self->model);
612 g_clear_object (&self->constraint);
613
614 G_OBJECT_CLASS (constraint_editor_parent_class)->dispose (object);
615}
616
617static void
618constraint_editor_class_init (ConstraintEditorClass *class)
619{
620 GObjectClass *object_class = G_OBJECT_CLASS (class);
621 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
622
623 object_class->constructed = constraint_editor_constructed;
624 object_class->dispose = constraint_editor_dispose;
625 object_class->set_property = constraint_editor_set_property;
626 object_class->get_property = constraint_editor_get_property;
627
628 pspecs[PROP_CONSTRAINT] =
629 g_param_spec_object (name: "constraint", nick: "constraint", blurb: "constraint",
630 GTK_TYPE_CONSTRAINT,
631 flags: G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
632
633 pspecs[PROP_MODEL] =
634 g_param_spec_object (name: "model", nick: "model", blurb: "model",
635 G_TYPE_LIST_MODEL,
636 flags: G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
637
638 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs);
639
640 signals[DONE] =
641 g_signal_new (signal_name: "done",
642 G_TYPE_FROM_CLASS (object_class),
643 signal_flags: G_SIGNAL_RUN_LAST,
644 class_offset: 0,
645 NULL, NULL,
646 NULL,
647 G_TYPE_NONE, n_params: 1, GTK_TYPE_CONSTRAINT);
648
649 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
650
651 gtk_widget_class_set_template_from_resource (widget_class,
652 resource_name: "/org/gtk/gtk4/constraint-editor/constraint-editor.ui");
653
654 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, grid);
655 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, target);
656 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, target_attr);
657 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, relation);
658 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, source);
659 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, source_attr);
660 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, multiplier);
661 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, constant);
662 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, strength);
663 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, preview);
664 gtk_widget_class_bind_template_child (widget_class, ConstraintEditor, button);
665
666 gtk_widget_class_bind_template_callback (widget_class, update_preview);
667 gtk_widget_class_bind_template_callback (widget_class, update_button);
668 gtk_widget_class_bind_template_callback (widget_class, create_constraint);
669 gtk_widget_class_bind_template_callback (widget_class, source_attr_changed);
670}
671
672ConstraintEditor *
673constraint_editor_new (GListModel *model,
674 GtkConstraint *constraint)
675{
676 return g_object_new (CONSTRAINT_EDITOR_TYPE,
677 first_property_name: "model", model,
678 "constraint", constraint,
679 NULL);
680}
681

source code of gtk/demos/constraint-editor/constraint-editor.c