1/*
2 * Copyright (c) 2017 Timm Bäder <mail@baedert.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12 * License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author: Timm Bäder <mail@baedert.org>
19 */
20
21/**
22 * GtkCenterBox:
23 *
24 * `GtkCenterBox` arranges three children in a row, keeping the middle child
25 * centered as well as possible.
26 *
27 * ![An example GtkCenterBox](centerbox.png)
28 *
29 * To add children to `GtkCenterBox`, use [method@Gtk.CenterBox.set_start_widget],
30 * [method@Gtk.CenterBox.set_center_widget] and
31 * [method@Gtk.CenterBox.set_end_widget].
32 *
33 * The sizing and positioning of children can be influenced with the
34 * align and expand properties of the children.
35 *
36 * # GtkCenterBox as GtkBuildable
37 *
38 * The `GtkCenterBox` implementation of the `GtkBuildable` interface
39 * supports placing children in the 3 positions by specifying “start”, “center”
40 * or “end” as the “type” attribute of a <child> element.
41 *
42 * # CSS nodes
43 *
44 * `GtkCenterBox` uses a single CSS node with the name “box”,
45 *
46 * The first child of the `GtkCenterBox` will be allocated depending on the
47 * text direction, i.e. in left-to-right layouts it will be allocated on the
48 * left and in right-to-left layouts on the right.
49 *
50 * In vertical orientation, the nodes of the children are arranged from top to
51 * bottom.
52 *
53 * # Accessibility
54 *
55 * `GtkCenterBox` uses the %GTK_ACCESSIBLE_ROLE_GROUP role.
56 */
57
58#include "config.h"
59#include "gtkcenterbox.h"
60#include "gtkcenterlayout.h"
61#include "gtkwidgetprivate.h"
62#include "gtkorientable.h"
63#include "gtkbuildable.h"
64#include "gtksizerequest.h"
65#include "gtktypebuiltins.h"
66#include "gtkprivate.h"
67#include "gtkintl.h"
68
69struct _GtkCenterBox
70{
71 GtkWidget parent_instance;
72
73 GtkWidget *start_widget;
74 GtkWidget *center_widget;
75 GtkWidget *end_widget;
76};
77
78struct _GtkCenterBoxClass
79{
80 GtkWidgetClass parent_class;
81};
82
83
84enum {
85 PROP_0,
86 PROP_BASELINE_POSITION,
87 PROP_ORIENTATION
88};
89
90static GtkBuildableIface *parent_buildable_iface;
91
92static void gtk_center_box_buildable_init (GtkBuildableIface *iface);
93
94G_DEFINE_TYPE_WITH_CODE (GtkCenterBox, gtk_center_box, GTK_TYPE_WIDGET,
95 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
96 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_center_box_buildable_init))
97
98static void
99gtk_center_box_buildable_add_child (GtkBuildable *buildable,
100 GtkBuilder *builder,
101 GObject *child,
102 const char *type)
103{
104 if (g_strcmp0 (str1: type, str2: "start") == 0)
105 gtk_center_box_set_start_widget (GTK_CENTER_BOX (buildable), GTK_WIDGET (child));
106 else if (g_strcmp0 (str1: type, str2: "center") == 0)
107 gtk_center_box_set_center_widget (GTK_CENTER_BOX (buildable), GTK_WIDGET (child));
108 else if (g_strcmp0 (str1: type, str2: "end") == 0)
109 gtk_center_box_set_end_widget (GTK_CENTER_BOX (buildable), GTK_WIDGET (child));
110 else
111 parent_buildable_iface->add_child (buildable, builder, child, type);
112}
113
114static void
115gtk_center_box_buildable_init (GtkBuildableIface *iface)
116{
117 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
118
119 iface->add_child = gtk_center_box_buildable_add_child;
120}
121
122static void
123gtk_center_box_set_property (GObject *object,
124 guint prop_id,
125 const GValue *value,
126 GParamSpec *pspec)
127{
128 GtkCenterBox *self = GTK_CENTER_BOX (object);
129 GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (object));
130
131 switch (prop_id)
132 {
133 case PROP_BASELINE_POSITION:
134 gtk_center_box_set_baseline_position (self, position: g_value_get_enum (value));
135 break;
136
137 case PROP_ORIENTATION:
138 {
139 GtkOrientation orientation = g_value_get_enum (value);
140 GtkOrientation current = gtk_center_layout_get_orientation (self: GTK_CENTER_LAYOUT (ptr: layout));
141 if (current != orientation)
142 {
143 gtk_center_layout_set_orientation (self: GTK_CENTER_LAYOUT (ptr: layout), orientation);
144 gtk_widget_update_orientation (GTK_WIDGET (self), orientation);
145 gtk_widget_queue_resize (GTK_WIDGET (self));
146 g_object_notify (object, property_name: "orientation");
147 }
148 }
149 break;
150
151 default:
152 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
153 break;
154 }
155}
156
157static void
158gtk_center_box_get_property (GObject *object,
159 guint prop_id,
160 GValue *value,
161 GParamSpec *pspec)
162{
163 GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (object));
164
165 switch (prop_id)
166 {
167 case PROP_BASELINE_POSITION:
168 g_value_set_enum (value, v_enum: gtk_center_layout_get_baseline_position (self: GTK_CENTER_LAYOUT (ptr: layout)));
169 break;
170
171 case PROP_ORIENTATION:
172 g_value_set_enum (value, v_enum: gtk_center_layout_get_orientation (self: GTK_CENTER_LAYOUT (ptr: layout)));
173 break;
174
175 default:
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177 break;
178 }
179}
180
181static void
182gtk_center_box_dispose (GObject *object)
183{
184 GtkCenterBox *self = GTK_CENTER_BOX (object);
185
186 g_clear_pointer (&self->start_widget, gtk_widget_unparent);
187 g_clear_pointer (&self->center_widget, gtk_widget_unparent);
188 g_clear_pointer (&self->end_widget, gtk_widget_unparent);
189
190 G_OBJECT_CLASS (gtk_center_box_parent_class)->dispose (object);
191}
192
193static void
194gtk_center_box_class_init (GtkCenterBoxClass *klass)
195{
196 GObjectClass *object_class = G_OBJECT_CLASS (klass);
197 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
198
199 object_class->set_property = gtk_center_box_set_property;
200 object_class->get_property = gtk_center_box_get_property;
201 object_class->dispose = gtk_center_box_dispose;
202
203 g_object_class_override_property (oclass: object_class, property_id: PROP_ORIENTATION, name: "orientation");
204
205 /**
206 * GtkCenterBox:baseline-position: (attributes org.gtk.Property.get=gtk_center_box_get_baseline_position org.gtk.Property.set=gtk_center_box_set_baseline_position)
207 *
208 * The position of the baseline aligned widget if extra space is available.
209 */
210 g_object_class_install_property (oclass: object_class, property_id: PROP_BASELINE_POSITION,
211 pspec: g_param_spec_enum (name: "baseline-position",
212 P_("Baseline position"),
213 P_("The position of the baseline aligned widgets if extra space is available"),
214 enum_type: GTK_TYPE_BASELINE_POSITION,
215 default_value: GTK_BASELINE_POSITION_CENTER,
216 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
217
218
219 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CENTER_LAYOUT);
220 gtk_widget_class_set_css_name (widget_class, I_("box"));
221 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP);
222}
223
224static void
225gtk_center_box_init (GtkCenterBox *self)
226{
227 self->start_widget = NULL;
228 self->center_widget = NULL;
229 self->end_widget = NULL;
230}
231
232/**
233 * gtk_center_box_new:
234 *
235 * Creates a new `GtkCenterBox`.
236 *
237 * Returns: the new `GtkCenterBox`.
238 */
239GtkWidget *
240gtk_center_box_new (void)
241{
242 return GTK_WIDGET (g_object_new (GTK_TYPE_CENTER_BOX, NULL));
243}
244
245/**
246 * gtk_center_box_set_start_widget:
247 * @self: a `GtkCenterBox`
248 * @child: (nullable): the new start widget
249 *
250 * Sets the start widget.
251 *
252 * To remove the existing start widget, pass %NULL.
253 */
254void
255gtk_center_box_set_start_widget (GtkCenterBox *self,
256 GtkWidget *child)
257{
258 GtkLayoutManager *layout_manager;
259
260 if (self->start_widget)
261 gtk_widget_unparent (widget: self->start_widget);
262
263 self->start_widget = child;
264 if (child)
265 gtk_widget_insert_after (widget: child, GTK_WIDGET (self), NULL);
266
267 layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
268 gtk_center_layout_set_start_widget (self: GTK_CENTER_LAYOUT (ptr: layout_manager), widget: child);
269}
270
271/**
272 * gtk_center_box_set_center_widget:
273 * @self: a `GtkCenterBox`
274 * @child: (nullable): the new center widget
275 *
276 * Sets the center widget.
277 *
278 * To remove the existing center widget, pas %NULL.
279 */
280void
281gtk_center_box_set_center_widget (GtkCenterBox *self,
282 GtkWidget *child)
283{
284 GtkLayoutManager *layout_manager;
285
286 if (self->center_widget)
287 gtk_widget_unparent (widget: self->center_widget);
288
289 self->center_widget = child;
290 if (child)
291 gtk_widget_insert_after (widget: child, GTK_WIDGET (self), previous_sibling: self->start_widget);
292
293 layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
294 gtk_center_layout_set_center_widget (self: GTK_CENTER_LAYOUT (ptr: layout_manager), widget: child);
295}
296
297/**
298 * gtk_center_box_set_end_widget:
299 * @self: a `GtkCenterBox`
300 * @child: (nullable): the new end widget
301 *
302 * Sets the end widget.
303 *
304 * To remove the existing end widget, pass %NULL.
305 */
306void
307gtk_center_box_set_end_widget (GtkCenterBox *self,
308 GtkWidget *child)
309{
310 GtkLayoutManager *layout_manager;
311
312 if (self->end_widget)
313 gtk_widget_unparent (widget: self->end_widget);
314
315 self->end_widget = child;
316 if (child)
317 gtk_widget_insert_before (widget: child, GTK_WIDGET (self), NULL);
318
319 layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
320 gtk_center_layout_set_end_widget (self: GTK_CENTER_LAYOUT (ptr: layout_manager), widget: child);
321}
322
323/**
324 * gtk_center_box_get_start_widget:
325 * @self: a `GtkCenterBox`
326 *
327 * Gets the start widget, or %NULL if there is none.
328 *
329 * Returns: (transfer none) (nullable): the start widget.
330 */
331GtkWidget *
332gtk_center_box_get_start_widget (GtkCenterBox *self)
333{
334 return self->start_widget;
335}
336
337/**
338 * gtk_center_box_get_center_widget:
339 * @self: a `GtkCenterBox`
340 *
341 * Gets the center widget, or %NULL if there is none.
342 *
343 * Returns: (transfer none) (nullable): the center widget.
344 */
345GtkWidget *
346gtk_center_box_get_center_widget (GtkCenterBox *self)
347{
348 return self->center_widget;
349}
350
351/**
352 * gtk_center_box_get_end_widget:
353 * @self: a `GtkCenterBox`
354 *
355 * Gets the end widget, or %NULL if there is none.
356 *
357 * Returns: (transfer none) (nullable): the end widget.
358 */
359GtkWidget *
360gtk_center_box_get_end_widget (GtkCenterBox *self)
361{
362 return self->end_widget;
363}
364
365/**
366 * gtk_center_box_set_baseline_position: (attributes org.gtk.Method.set_property=baseline-position)
367 * @self: a `GtkCenterBox`
368 * @position: a `GtkBaselinePosition`
369 *
370 * Sets the baseline position of a center box.
371 *
372 * This affects only horizontal boxes with at least one baseline
373 * aligned child. If there is more vertical space available than
374 * requested, and the baseline is not allocated by the parent then
375 * @position is used to allocate the baseline wrt. the extra space
376 * available.
377 */
378void
379gtk_center_box_set_baseline_position (GtkCenterBox *self,
380 GtkBaselinePosition position)
381{
382 GtkBaselinePosition current_position;
383 GtkLayoutManager *layout;
384
385 g_return_if_fail (GTK_IS_CENTER_BOX (self));
386
387 layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
388 current_position = gtk_center_layout_get_baseline_position (self: GTK_CENTER_LAYOUT (ptr: layout));
389 if (current_position != position)
390 {
391 gtk_center_layout_set_baseline_position (self: GTK_CENTER_LAYOUT (ptr: layout), baseline_position: position);
392 g_object_notify (G_OBJECT (self), property_name: "baseline-position");
393 gtk_widget_queue_resize (GTK_WIDGET (self));
394 }
395}
396
397/**
398 * gtk_center_box_get_baseline_position: (attributes org.gtk.Method.get_property=baseline-position)
399 * @self: a `GtkCenterBox`
400 *
401 * Gets the value set by gtk_center_box_set_baseline_position().
402 *
403 * Returns: the baseline position
404 */
405GtkBaselinePosition
406gtk_center_box_get_baseline_position (GtkCenterBox *self)
407{
408 GtkLayoutManager *layout;
409
410 g_return_val_if_fail (GTK_IS_CENTER_BOX (self), GTK_BASELINE_POSITION_CENTER);
411
412 layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
413
414 return gtk_center_layout_get_baseline_position (self: GTK_CENTER_LAYOUT (ptr: layout));
415}
416
417

source code of gtk/gtk/gtkcenterbox.c