1 /* Constraints/VFL
2  *
3  * GtkConstraintLayout allows defining constraints using a
4  * compact syntax called Visual Format Language, or VFL.
5  *
6  * A typical example of a VFL specification looks like this:
7  *
8  * H:|-[button1(==button2)]-12-[button2]-|
9  */
10 
11 #include <glib/gi18n.h>
12 #include <gtk/gtk.h>
13 
14 G_DECLARE_FINAL_TYPE (VflGrid, vfl_grid, VFL, GRID, GtkWidget)
15 
16 struct _VflGrid
17 {
18   GtkWidget parent_instance;
19 
20   GtkWidget *button1, *button2;
21   GtkWidget *button3;
22 };
23 
G_DEFINE_TYPE(VflGrid,vfl_grid,GTK_TYPE_WIDGET)24 G_DEFINE_TYPE (VflGrid, vfl_grid, GTK_TYPE_WIDGET)
25 
26 static void
27 vfl_grid_dispose (GObject *object)
28 {
29   VflGrid *self = VFL_GRID (object);
30 
31   g_clear_pointer (&self->button1, gtk_widget_unparent);
32   g_clear_pointer (&self->button2, gtk_widget_unparent);
33   g_clear_pointer (&self->button3, gtk_widget_unparent);
34 
35   G_OBJECT_CLASS (vfl_grid_parent_class)->dispose (object);
36 }
37 
38 static void
vfl_grid_class_init(VflGridClass * klass)39 vfl_grid_class_init (VflGridClass *klass)
40 {
41   GObjectClass *object_class = G_OBJECT_CLASS (klass);
42   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
43 
44   object_class->dispose = vfl_grid_dispose;
45 
46   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CONSTRAINT_LAYOUT);
47 }
48 
49 /* Layout:
50  *
51  *   +-----------------------------+
52  *   | +-----------+ +-----------+ |
53  *   | |  Child 1  | |  Child 2  | |
54  *   | +-----------+ +-----------+ |
55  *   | +-------------------------+ |
56  *   | |         Child 3         | |
57  *   | +-------------------------+ |
58  *   +-----------------------------+
59  *
60  * Constraints:
61  *
62  *   super.start = child1.start - 8
63  *   child1.width = child2.width
64  *   child1.end = child2.start - 12
65  *   child2.end = super.end - 8
66  *   super.start = child3.start - 8
67  *   child3.end = super.end - 8
68  *   super.top = child1.top - 8
69  *   super.top = child2.top - 8
70  *   child1.bottom = child3.top - 12
71  *   child2.bottom = child3.top - 12
72  *   child3.height = child1.height
73  *   child3.height = child2.height
74  *   child3.bottom = super.bottom - 8
75  *
76  * Visual format:
77  *
78  *   H:|-8-[view1(==view2)-12-[view2]-8-|
79  *   H:|-8-[view3]-8-|
80  *   V:|-8-[view1]-12-[view3(==view1)]-8-|
81  *   V:|-8-[view2]-12-[view3(==view2)]-8-|
82  */
83 static void
build_constraints(VflGrid * self,GtkConstraintLayout * manager)84 build_constraints (VflGrid          *self,
85                    GtkConstraintLayout *manager)
86 {
87   const char * const vfl[] = {
88     "H:|-[button1(==button2)]-12-[button2]-|",
89     "H:|-[button3]-|",
90     "V:|-[button1]-12-[button3(==button1)]-|",
91     "V:|-[button2]-12-[button3(==button2)]-|",
92   };
93   GError *error = NULL;
94 
95   gtk_constraint_layout_add_constraints_from_description (manager, vfl, G_N_ELEMENTS (vfl),
96                                                           8, 8,
97                                                           &error,
98                                                           "button1", self->button1,
99                                                           "button2", self->button2,
100                                                           "button3", self->button3,
101                                                           NULL);
102   if (error != NULL)
103     {
104       g_printerr ("VFL parsing error:\n%s", error->message);
105       g_error_free (error);
106     }
107 }
108 
109 static void
vfl_grid_init(VflGrid * self)110 vfl_grid_init (VflGrid *self)
111 {
112   GtkWidget *widget = GTK_WIDGET (self);
113 
114   self->button1 = gtk_button_new_with_label ("Child 1");
115   gtk_widget_set_parent (self->button1, widget);
116   gtk_widget_set_name (self->button1, "button1");
117 
118   self->button2 = gtk_button_new_with_label ("Child 2");
119   gtk_widget_set_parent (self->button2, widget);
120   gtk_widget_set_name (self->button2, "button2");
121 
122   self->button3 = gtk_button_new_with_label ("Child 3");
123   gtk_widget_set_parent (self->button3, widget);
124   gtk_widget_set_name (self->button3, "button3");
125 
126   GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
127   build_constraints (self, GTK_CONSTRAINT_LAYOUT (manager));
128 }
129 
130 GtkWidget *
do_constraints_vfl(GtkWidget * do_widget)131 do_constraints_vfl (GtkWidget *do_widget)
132 {
133  static GtkWidget *window;
134 
135  if (!window)
136    {
137      GtkWidget *box, *grid;
138 
139      window = gtk_window_new ();
140      gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
141      gtk_window_set_title (GTK_WINDOW (window), "Constraints — VFL");
142      gtk_window_set_default_size (GTK_WINDOW (window), 260, -1);
143      g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
144 
145      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
146      gtk_window_set_child (GTK_WINDOW (window), box);
147 
148      grid = g_object_new (vfl_grid_get_type (), NULL);
149      gtk_widget_set_hexpand (grid, TRUE);
150      gtk_widget_set_vexpand (grid, TRUE);
151      gtk_box_append (GTK_BOX (box), grid);
152    }
153 
154  if (!gtk_widget_get_visible (window))
155    gtk_widget_show (window);
156  else
157    gtk_window_destroy (GTK_WINDOW (window));
158 
159  return window;
160 }
161