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 "guide-editor.h"
23
24struct _GuideEditor
25{
26 GtkWidget parent_instance;
27
28 GtkWidget *grid;
29 GtkWidget *name;
30 GtkWidget *min_width;
31 GtkWidget *min_height;
32 GtkWidget *nat_width;
33 GtkWidget *nat_height;
34 GtkWidget *max_width;
35 GtkWidget *max_height;
36 GtkWidget *strength;
37 GtkWidget *button;
38
39 GtkConstraintGuide *guide;
40
41 gboolean constructed;
42};
43
44enum {
45 PROP_GUIDE = 1,
46 LAST_PROP
47};
48
49static GParamSpec *pspecs[LAST_PROP];
50
51enum {
52 DONE,
53 LAST_SIGNAL
54};
55
56static guint signals[LAST_SIGNAL];
57
58G_DEFINE_TYPE(GuideEditor, guide_editor, GTK_TYPE_WIDGET);
59
60static void
61guide_strength_combo (GtkWidget *combo)
62{
63 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "weak", text: "Weak");
64 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "medium", text: "Medium");
65 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "strong", text: "Strong");
66 gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo), id: "required", text: "Required");
67}
68
69static GtkConstraintStrength
70get_strength (const char *id)
71{
72 GtkConstraintStrength strength;
73 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_STRENGTH);
74 GEnumValue *value = g_enum_get_value_by_nick (enum_class: class, nick: id);
75 strength = value->value;
76 g_type_class_unref (g_class: class);
77
78 return strength;
79}
80
81static const char *
82get_strength_nick (GtkConstraintStrength strength)
83{
84 GEnumClass *class = g_type_class_ref (type: GTK_TYPE_CONSTRAINT_STRENGTH);
85 GEnumValue *value = g_enum_get_value (enum_class: class, value: strength);
86 const char *nick = value->value_nick;
87 g_type_class_unref (g_class: class);
88
89 return nick;
90}
91
92void
93guide_editor_serialize_guide (GString *str,
94 int indent,
95 GtkConstraintGuide *guide)
96{
97 int min_width, min_height;
98 int nat_width, nat_height;
99 int max_width, max_height;
100 const char *name;
101 const char *strength;
102
103 gtk_constraint_guide_get_min_size (guide, width: &min_width, height: &min_height);
104 gtk_constraint_guide_get_nat_size (guide, width: &nat_width, height: &nat_height);
105 gtk_constraint_guide_get_max_size (guide, width: &max_width, height: &max_height);
106 name = gtk_constraint_guide_get_name (guide);
107 strength = get_strength_nick (strength: gtk_constraint_guide_get_strength (guide));
108
109 g_string_append_printf (string: str, format: "%*s<guide min-width=\"%d\" min-height=\"%d\"\n", indent, "", min_width, min_height);
110 g_string_append_printf (string: str, format: "%*s nat-width=\"%d\" nat-height=\"%d\"\n", indent, "", nat_width, nat_height);
111 g_string_append_printf (string: str, format: "%*s max-width=\"%d\" max-height=\"%d\"\n", indent, "", max_width, max_height);
112 g_string_append_printf (string: str, format: "%*s name=\"%s\" strength=\"%s\" />\n", indent, "", name, strength);
113}
114
115static void
116create_guide (GtkButton *button,
117 GuideEditor *editor)
118{
119 const char *id;
120 int strength;
121 const char *name;
122 int w, h;
123 GtkConstraintGuide *guide;
124
125 if (editor->guide)
126 guide = g_object_ref (editor->guide);
127 else
128 guide = gtk_constraint_guide_new ();
129
130 name = gtk_editable_get_text (GTK_EDITABLE (editor->name));
131 gtk_constraint_guide_set_name (guide, name);
132
133 w = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->min_width));
134 h = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->min_height));
135 gtk_constraint_guide_set_min_size (guide, width: w, height: h);
136
137 w = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->nat_width));
138 h = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->nat_height));
139 gtk_constraint_guide_set_nat_size (guide, width: w, height: h);
140
141 w = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->max_width));
142 h = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editor->max_height));
143 gtk_constraint_guide_set_max_size (guide, width: w, height: h);
144
145 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (editor->strength));
146 strength = get_strength (id);
147 gtk_constraint_guide_set_strength (guide, strength);
148
149 g_signal_emit (instance: editor, signal_id: signals[DONE], detail: 0, guide);
150 g_object_unref (object: guide);
151}
152
153static void
154guide_editor_init (GuideEditor *editor)
155{
156 gtk_widget_init_template (GTK_WIDGET (editor));
157}
158
159static int guide_counter;
160
161static int
162min_input (GtkSpinButton *spin_button,
163 double *new_val)
164{
165 if (strcmp (s1: gtk_editable_get_text (GTK_EDITABLE (spin_button)), s2: "") == 0)
166 {
167 *new_val = 0.0;
168 return TRUE;
169 }
170
171 return FALSE;
172}
173
174static int
175max_input (GtkSpinButton *spin_button,
176 double *new_val)
177{
178 if (strcmp (s1: gtk_editable_get_text (GTK_EDITABLE (spin_button)), s2: "") == 0)
179 {
180 *new_val = G_MAXINT;
181 return TRUE;
182 }
183
184 return FALSE;
185}
186
187static void
188guide_editor_constructed (GObject *object)
189{
190 GuideEditor *editor = GUIDE_EDITOR (ptr: object);
191
192 guide_strength_combo (combo: editor->strength);
193
194 g_signal_connect (editor->min_width, "input", G_CALLBACK (min_input), NULL);
195
196 g_signal_connect (editor->min_height, "input", G_CALLBACK (min_input), NULL);
197
198 g_signal_connect (editor->max_width, "input", G_CALLBACK (max_input), NULL);
199
200 g_signal_connect (editor->max_height, "input", G_CALLBACK (max_input), NULL);
201
202 if (editor->guide)
203 {
204 GtkConstraintStrength strength;
205 const char *nick;
206 int w, h;
207
208 nick = gtk_constraint_guide_get_name (guide: editor->guide);
209 if (nick)
210 gtk_editable_set_text (GTK_EDITABLE (editor->name), text: nick);
211
212 gtk_constraint_guide_get_min_size (guide: editor->guide, width: &w, height: &h);
213 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_width), value: w);
214 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_height), value: h);
215
216 gtk_constraint_guide_get_nat_size (guide: editor->guide, width: &w, height: &h);
217 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_width), value: w);
218 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_height), value: h);
219
220 gtk_constraint_guide_get_max_size (guide: editor->guide, width: &w, height: &h);
221 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_width), value: w);
222 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_height), value: h);
223
224 strength = gtk_constraint_guide_get_strength (guide: editor->guide);
225 nick = get_strength_nick (strength);
226 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), active_id: nick);
227
228 gtk_button_set_label (GTK_BUTTON (editor->button), label: "Apply");
229 }
230 else
231 {
232 char *name;
233
234 guide_counter++;
235 name = g_strdup_printf (format: "Guide %d", guide_counter);
236 gtk_editable_set_text (GTK_EDITABLE (editor->name), text: name);
237 g_free (mem: name);
238
239 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_width), value: 0.0);
240 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->min_height), value: 0.0);
241 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_width), value: 0.0);
242 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->nat_height), value: 0.0);
243 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_width), G_MAXINT);
244 gtk_spin_button_set_value (GTK_SPIN_BUTTON (editor->max_height), G_MAXINT);
245
246 gtk_combo_box_set_active_id (GTK_COMBO_BOX (editor->strength), active_id: "medium");
247
248 gtk_button_set_label (GTK_BUTTON (editor->button), label: "Create");
249 }
250
251 editor->constructed = TRUE;
252}
253
254static void
255guide_editor_set_property (GObject *object,
256 guint property_id,
257 const GValue *value,
258 GParamSpec *pspec)
259{
260 GuideEditor *self = GUIDE_EDITOR (ptr: object);
261
262 switch (property_id)
263 {
264 case PROP_GUIDE:
265 self->guide = g_value_dup_object (value);
266 break;
267
268 default:
269 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
270 }
271}
272
273static void
274guide_editor_get_property (GObject *object,
275 guint property_id,
276 GValue *value,
277 GParamSpec *pspec)
278{
279 GuideEditor *self = GUIDE_EDITOR (ptr: object);
280
281 switch (property_id)
282 {
283 case PROP_GUIDE:
284 g_value_set_object (value, v_object: self->guide);
285 break;
286
287 default:
288 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
289 }
290}
291
292static void
293guide_editor_dispose (GObject *object)
294{
295 GuideEditor *self = (GuideEditor *)object;
296
297 g_clear_pointer (&self->grid, gtk_widget_unparent);
298 g_clear_object (&self->guide);
299
300 G_OBJECT_CLASS (guide_editor_parent_class)->dispose (object);
301}
302
303static void
304guide_editor_class_init (GuideEditorClass *class)
305{
306 GObjectClass *object_class = G_OBJECT_CLASS (class);
307 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
308
309 object_class->constructed = guide_editor_constructed;
310 object_class->dispose = guide_editor_dispose;
311 object_class->set_property = guide_editor_set_property;
312 object_class->get_property = guide_editor_get_property;
313
314 pspecs[PROP_GUIDE] =
315 g_param_spec_object (name: "guide", nick: "guide", blurb: "guide",
316 GTK_TYPE_CONSTRAINT_GUIDE,
317 flags: G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
318
319 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs);
320
321 signals[DONE] =
322 g_signal_new (signal_name: "done",
323 G_TYPE_FROM_CLASS (object_class),
324 signal_flags: G_SIGNAL_RUN_LAST,
325 class_offset: 0,
326 NULL, NULL,
327 NULL,
328 G_TYPE_NONE, n_params: 1, GTK_TYPE_CONSTRAINT_GUIDE);
329
330 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
331
332 gtk_widget_class_set_template_from_resource (widget_class,
333 resource_name: "/org/gtk/gtk4/constraint-editor/guide-editor.ui");
334
335 gtk_widget_class_bind_template_child (widget_class, GuideEditor, grid);
336 gtk_widget_class_bind_template_child (widget_class, GuideEditor, name);
337 gtk_widget_class_bind_template_child (widget_class, GuideEditor, min_width);
338 gtk_widget_class_bind_template_child (widget_class, GuideEditor, min_height);
339 gtk_widget_class_bind_template_child (widget_class, GuideEditor, nat_width);
340 gtk_widget_class_bind_template_child (widget_class, GuideEditor, nat_height);
341 gtk_widget_class_bind_template_child (widget_class, GuideEditor, max_width);
342 gtk_widget_class_bind_template_child (widget_class, GuideEditor, max_height);
343 gtk_widget_class_bind_template_child (widget_class, GuideEditor, strength);
344 gtk_widget_class_bind_template_child (widget_class, GuideEditor, button);
345
346 gtk_widget_class_bind_template_callback (widget_class, create_guide);
347}
348
349GuideEditor *
350guide_editor_new (GtkConstraintGuide *guide)
351{
352 return g_object_new (GUIDE_EDITOR_TYPE,
353 first_property_name: "guide", guide,
354 NULL);
355}
356

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