1 | /* Constraints/Interactive Constraints |
2 | * #Keywords: GtkConstraintLayout |
3 | * |
4 | * This example shows how constraints can be updated during user interaction. |
5 | * The vertical edge between the buttons can be dragged with the mouse. |
6 | */ |
7 | |
8 | #include <glib/gi18n.h> |
9 | #include <gtk/gtk.h> |
10 | |
11 | G_DECLARE_FINAL_TYPE (InteractiveGrid, interactive_grid, INTERACTIVE, GRID, GtkWidget) |
12 | |
13 | struct _InteractiveGrid |
14 | { |
15 | GtkWidget parent_instance; |
16 | |
17 | GtkWidget *button1, *button2; |
18 | GtkWidget *button3; |
19 | GtkConstraintGuide *guide; |
20 | GtkConstraint *constraint; |
21 | }; |
22 | |
23 | G_DEFINE_TYPE (InteractiveGrid, interactive_grid, GTK_TYPE_WIDGET) |
24 | |
25 | static void |
26 | interactive_grid_dispose (GObject *object) |
27 | { |
28 | InteractiveGrid *self = INTERACTIVE_GRID (ptr: object); |
29 | |
30 | g_clear_pointer (&self->button1, gtk_widget_unparent); |
31 | g_clear_pointer (&self->button2, gtk_widget_unparent); |
32 | g_clear_pointer (&self->button3, gtk_widget_unparent); |
33 | |
34 | G_OBJECT_CLASS (interactive_grid_parent_class)->dispose (object); |
35 | } |
36 | |
37 | static void |
38 | interactive_grid_class_init (InteractiveGridClass *klass) |
39 | { |
40 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
41 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
42 | |
43 | object_class->dispose = interactive_grid_dispose; |
44 | |
45 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT); |
46 | } |
47 | |
48 | static void |
49 | build_constraints (InteractiveGrid *self, |
50 | GtkConstraintLayout *manager) |
51 | { |
52 | self->guide = g_object_new (GTK_TYPE_CONSTRAINT_GUIDE, NULL); |
53 | gtk_constraint_layout_add_guide (layout: manager, guide: self->guide); |
54 | |
55 | gtk_constraint_layout_add_constraint (layout: manager, |
56 | constraint: gtk_constraint_new_constant (target: GTK_CONSTRAINT_TARGET (ptr: self->guide), |
57 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_WIDTH, |
58 | relation: GTK_CONSTRAINT_RELATION_EQ, |
59 | constant: 0.0, |
60 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
61 | |
62 | gtk_constraint_layout_add_constraint (layout: manager, |
63 | constraint: gtk_constraint_new (NULL, |
64 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
65 | relation: GTK_CONSTRAINT_RELATION_EQ, |
66 | source: GTK_CONSTRAINT_TARGET (ptr: self->button1), |
67 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
68 | multiplier: 1.0, |
69 | constant: -8.0, |
70 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
71 | gtk_constraint_layout_add_constraint (layout: manager, |
72 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button1), |
73 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_END, |
74 | relation: GTK_CONSTRAINT_RELATION_EQ, |
75 | source: GTK_CONSTRAINT_TARGET (ptr: self->guide), |
76 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
77 | multiplier: 1.0, |
78 | constant: 0.0, |
79 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
80 | gtk_constraint_layout_add_constraint (layout: manager, |
81 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button2), |
82 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
83 | relation: GTK_CONSTRAINT_RELATION_EQ, |
84 | source: GTK_CONSTRAINT_TARGET (ptr: self->guide), |
85 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_END, |
86 | multiplier: 1.0, |
87 | constant: 0.0, |
88 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
89 | gtk_constraint_layout_add_constraint (layout: manager, |
90 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button2), |
91 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_END, |
92 | relation: GTK_CONSTRAINT_RELATION_EQ, |
93 | NULL, |
94 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_END, |
95 | multiplier: 1.0, |
96 | constant: -8.0, |
97 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
98 | gtk_constraint_layout_add_constraint (layout: manager, |
99 | constraint: gtk_constraint_new (NULL, |
100 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
101 | relation: GTK_CONSTRAINT_RELATION_EQ, |
102 | source: GTK_CONSTRAINT_TARGET (ptr: self->button3), |
103 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
104 | multiplier: 1.0, |
105 | constant: -8.0, |
106 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
107 | |
108 | gtk_constraint_layout_add_constraint (layout: manager, |
109 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button3), |
110 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_END, |
111 | relation: GTK_CONSTRAINT_RELATION_EQ, |
112 | source: GTK_CONSTRAINT_TARGET (ptr: self->guide), |
113 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_START, |
114 | multiplier: 1.0, |
115 | constant: 0.0, |
116 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
117 | |
118 | gtk_constraint_layout_add_constraint (layout: manager, |
119 | constraint: gtk_constraint_new (NULL, |
120 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_TOP, |
121 | relation: GTK_CONSTRAINT_RELATION_EQ, |
122 | source: GTK_CONSTRAINT_TARGET (ptr: self->button1), |
123 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_TOP, |
124 | multiplier: 1.0, |
125 | constant: -8.0, |
126 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
127 | gtk_constraint_layout_add_constraint (layout: manager, |
128 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button2), |
129 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_TOP, |
130 | relation: GTK_CONSTRAINT_RELATION_EQ, |
131 | source: GTK_CONSTRAINT_TARGET (ptr: self->button1), |
132 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_BOTTOM, |
133 | multiplier: 1.0, |
134 | constant: 0.0, |
135 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
136 | gtk_constraint_layout_add_constraint (layout: manager, |
137 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button3), |
138 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_TOP, |
139 | relation: GTK_CONSTRAINT_RELATION_EQ, |
140 | source: GTK_CONSTRAINT_TARGET (ptr: self->button2), |
141 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_BOTTOM, |
142 | multiplier: 1.0, |
143 | constant: 0.0, |
144 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
145 | gtk_constraint_layout_add_constraint (layout: manager, |
146 | constraint: gtk_constraint_new (target: GTK_CONSTRAINT_TARGET (ptr: self->button3), |
147 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_BOTTOM, |
148 | relation: GTK_CONSTRAINT_RELATION_EQ, |
149 | NULL, |
150 | source_attribute: GTK_CONSTRAINT_ATTRIBUTE_BOTTOM, |
151 | multiplier: 1.0, |
152 | constant: -8.0, |
153 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED)); |
154 | } |
155 | |
156 | static void |
157 | drag_cb (GtkGestureDrag *drag, |
158 | double offset_x, |
159 | double offset_y, |
160 | InteractiveGrid *self) |
161 | { |
162 | GtkConstraintLayout *layout = GTK_CONSTRAINT_LAYOUT (ptr: gtk_widget_get_layout_manager (GTK_WIDGET (self))); |
163 | double x, y; |
164 | |
165 | if (self->constraint) |
166 | { |
167 | gtk_constraint_layout_remove_constraint (layout, constraint: self->constraint); |
168 | g_clear_object (&self->constraint); |
169 | } |
170 | |
171 | gtk_gesture_drag_get_start_point (gesture: drag, x: &x, y: &y); |
172 | self->constraint = gtk_constraint_new_constant (target: GTK_CONSTRAINT_TARGET (ptr: self->guide), |
173 | target_attribute: GTK_CONSTRAINT_ATTRIBUTE_LEFT, |
174 | relation: GTK_CONSTRAINT_RELATION_EQ, |
175 | constant: x + offset_x, |
176 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
177 | gtk_constraint_layout_add_constraint (layout, g_object_ref (self->constraint)); |
178 | gtk_widget_queue_allocate (GTK_WIDGET (self)); |
179 | } |
180 | |
181 | static void |
182 | interactive_grid_init (InteractiveGrid *self) |
183 | { |
184 | GtkWidget *widget = GTK_WIDGET (self); |
185 | GtkGesture *drag; |
186 | |
187 | self->button1 = gtk_button_new_with_label (label: "Child 1" ); |
188 | gtk_widget_set_parent (widget: self->button1, parent: widget); |
189 | gtk_widget_set_name (widget: self->button1, name: "button1" ); |
190 | |
191 | self->button2 = gtk_button_new_with_label (label: "Child 2" ); |
192 | gtk_widget_set_parent (widget: self->button2, parent: widget); |
193 | gtk_widget_set_name (widget: self->button2, name: "button2" ); |
194 | |
195 | self->button3 = gtk_button_new_with_label (label: "Child 3" ); |
196 | gtk_widget_set_parent (widget: self->button3, parent: widget); |
197 | gtk_widget_set_name (widget: self->button3, name: "button3" ); |
198 | |
199 | GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self)); |
200 | build_constraints (self, manager: GTK_CONSTRAINT_LAYOUT (ptr: manager)); |
201 | |
202 | drag = gtk_gesture_drag_new (); |
203 | g_signal_connect (drag, "drag-update" , G_CALLBACK (drag_cb), self); |
204 | gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag)); |
205 | } |
206 | |
207 | GtkWidget * |
208 | do_constraints_interactive (GtkWidget *do_widget) |
209 | { |
210 | static GtkWidget *window; |
211 | |
212 | if (!window) |
213 | { |
214 | GtkWidget *box, *grid; |
215 | |
216 | window = gtk_window_new (); |
217 | gtk_window_set_display (GTK_WINDOW (window), display: gtk_widget_get_display (widget: do_widget)); |
218 | gtk_window_set_title (GTK_WINDOW (window), title: "Interactive Constraints" ); |
219 | gtk_window_set_default_size (GTK_WINDOW (window), width: 260, height: -1); |
220 | g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window); |
221 | |
222 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12); |
223 | gtk_window_set_child (GTK_WINDOW (window), child: box); |
224 | |
225 | grid = g_object_new (object_type: interactive_grid_get_type (), NULL); |
226 | gtk_widget_set_hexpand (widget: grid, TRUE); |
227 | gtk_widget_set_vexpand (widget: grid, TRUE); |
228 | gtk_box_append (GTK_BOX (box), child: grid); |
229 | } |
230 | |
231 | if (!gtk_widget_get_visible (widget: window)) |
232 | gtk_widget_show (widget: window); |
233 | else |
234 | gtk_window_destroy (GTK_WINDOW (window)); |
235 | |
236 | return window; |
237 | } |
238 | |