1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Red Hat, Inc.
3 * Author: Matthias Clasen
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "config.h"
20
21#include <string.h>
22
23#include "gtkgrid.h"
24
25#include "gtkbuildable.h"
26#include "gtkcsspositionvalueprivate.h"
27#include "gtkgridlayout.h"
28#include "gtkintl.h"
29#include "gtkorientable.h"
30#include "gtkprivate.h"
31#include "gtksizerequest.h"
32#include "gtkcssnodeprivate.h"
33#include "gtkwidgetprivate.h"
34
35
36/**
37 * GtkGrid:
38 *
39 * `GtkGrid` is a container which arranges its child widgets in
40 * rows and columns.
41 *
42 * ![An example GtkGrid](grid.png)
43 *
44 * It supports arbitrary positions and horizontal/vertical spans.
45 *
46 * Children are added using [method@Gtk.Grid.attach]. They can span multiple
47 * rows or columns. It is also possible to add a child next to an existing
48 * child, using [method@Gtk.Grid.attach_next_to]. To remove a child from the
49 * grid, use [method@Gtk.Grid.remove].
50 *
51 * The behaviour of `GtkGrid` when several children occupy the same grid
52 * cell is undefined.
53 *
54 * # GtkGrid as GtkBuildable
55 *
56 * Every child in a `GtkGrid` has access to a custom [iface@Gtk.Buildable]
57 * element, called `<layout>`. It can by used to specify a position in the
58 * grid and optionally spans. All properties that can be used in the `<layout>`
59 * element are implemented by [class@Gtk.GridLayoutChild].
60 *
61 * It is implemented by `GtkWidget` using [class@Gtk.LayoutManager].
62 *
63 * To showcase it, here is a simple example:
64 *
65 * ```xml
66 * <object class="GtkGrid" id="my_grid">
67 * <child>
68 * <object class="GtkButton" id="button1">
69 * <property name="label">Button 1</property>
70 * <layout>
71 * <property name="column">0</property>
72 * <property name="row">0</property>
73 * </layout>
74 * </object>
75 * </child>
76 * <child>
77 * <object class="GtkButton" id="button2">
78 * <property name="label">Button 2</property>
79 * <layout>
80 * <property name="column">1</property>
81 * <property name="row">0</property>
82 * </layout>
83 * </object>
84 * </child>
85 * <child>
86 * <object class="GtkButton" id="button3">
87 * <property name="label">Button 3</property>
88 * <layout>
89 * <property name="column">2</property>
90 * <property name="row">0</property>
91 * <property name="row-span">2</property>
92 * </layout>
93 * </object>
94 * </child>
95 * <child>
96 * <object class="GtkButton" id="button4">
97 * <property name="label">Button 4</property>
98 * <layout>
99 * <property name="column">0</property>
100 * <property name="row">1</property>
101 * <property name="column-span">2</property>
102 * </layout>
103 * </object>
104 * </child>
105 * </object>
106 * ```
107 *
108 * It organizes the first two buttons side-by-side in one cell each.
109 * The third button is in the last column but spans across two rows.
110 * This is defined by the `row-span` property. The last button is
111 * located in the second row and spans across two columns, which is
112 * defined by the `column-span` property.
113 *
114 * # CSS nodes
115 *
116 * `GtkGrid` uses a single CSS node with name `grid`.
117 *
118 * # Accessibility
119 *
120 * `GtkGrid` uses the %GTK_ACCESSIBLE_ROLE_GROUP role.
121 */
122
123typedef struct
124{
125 GtkLayoutManager *layout_manager;
126
127 GtkOrientation orientation;
128} GtkGridPrivate;
129
130enum
131{
132 PROP_0,
133 PROP_ROW_SPACING,
134 PROP_COLUMN_SPACING,
135 PROP_ROW_HOMOGENEOUS,
136 PROP_COLUMN_HOMOGENEOUS,
137 PROP_BASELINE_ROW,
138 N_PROPERTIES,
139
140 /* GtkOrientable */
141 PROP_ORIENTATION
142};
143
144static void gtk_grid_buildable_iface_init (GtkBuildableIface *iface);
145
146static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
147
148G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_WIDGET,
149 G_ADD_PRIVATE (GtkGrid)
150 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
151 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
152 gtk_grid_buildable_iface_init))
153
154
155static void
156gtk_grid_get_property (GObject *object,
157 guint prop_id,
158 GValue *value,
159 GParamSpec *pspec)
160{
161 GtkGrid *grid = GTK_GRID (object);
162 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
163
164 switch (prop_id)
165 {
166 case PROP_ORIENTATION:
167 g_value_set_enum (value, v_enum: priv->orientation);
168 break;
169
170 case PROP_ROW_SPACING:
171 g_value_set_int (value, v_int: gtk_grid_layout_get_row_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager)));
172 break;
173
174 case PROP_COLUMN_SPACING:
175 g_value_set_int (value, v_int: gtk_grid_layout_get_column_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager)));
176 break;
177
178 case PROP_ROW_HOMOGENEOUS:
179 g_value_set_boolean (value, v_boolean: gtk_grid_layout_get_row_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager)));
180 break;
181
182 case PROP_COLUMN_HOMOGENEOUS:
183 g_value_set_boolean (value, v_boolean: gtk_grid_layout_get_column_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager)));
184 break;
185
186 case PROP_BASELINE_ROW:
187 g_value_set_int (value, v_int: gtk_grid_layout_get_baseline_row (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager)));
188 break;
189
190 default:
191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
192 break;
193 }
194}
195
196static void
197gtk_grid_set_orientation (GtkGrid *grid,
198 GtkOrientation orientation)
199{
200 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
201
202 if (priv->orientation != orientation)
203 {
204 priv->orientation = orientation;
205
206 gtk_widget_update_orientation (GTK_WIDGET (grid), orientation: priv->orientation);
207
208 g_object_notify (G_OBJECT (grid), property_name: "orientation");
209 }
210}
211
212static void
213gtk_grid_set_property (GObject *object,
214 guint prop_id,
215 const GValue *value,
216 GParamSpec *pspec)
217{
218 GtkGrid *grid = GTK_GRID (object);
219
220 switch (prop_id)
221 {
222 case PROP_ORIENTATION:
223 gtk_grid_set_orientation (grid, orientation: g_value_get_enum (value));
224 break;
225
226 case PROP_ROW_SPACING:
227 gtk_grid_set_row_spacing (grid, spacing: g_value_get_int (value));
228 break;
229
230 case PROP_COLUMN_SPACING:
231 gtk_grid_set_column_spacing (grid, spacing: g_value_get_int (value));
232 break;
233
234 case PROP_ROW_HOMOGENEOUS:
235 gtk_grid_set_row_homogeneous (grid, homogeneous: g_value_get_boolean (value));
236 break;
237
238 case PROP_COLUMN_HOMOGENEOUS:
239 gtk_grid_set_column_homogeneous (grid, homogeneous: g_value_get_boolean (value));
240 break;
241
242 case PROP_BASELINE_ROW:
243 gtk_grid_set_baseline_row (grid, row: g_value_get_int (value));
244 break;
245
246 default:
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248 break;
249 }
250}
251
252static void
253grid_attach (GtkGrid *grid,
254 GtkWidget *widget,
255 int column,
256 int row,
257 int width,
258 int height)
259{
260 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
261 GtkGridLayoutChild *grid_child;
262
263 gtk_widget_set_parent (widget, GTK_WIDGET (grid));
264
265 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child: widget));
266 gtk_grid_layout_child_set_column (child: grid_child, column);
267 gtk_grid_layout_child_set_row (child: grid_child, row);
268 gtk_grid_layout_child_set_column_span (child: grid_child, span: width);
269 gtk_grid_layout_child_set_row_span (child: grid_child, span: height);
270}
271
272/* Find the position 'touching' existing
273 * children. @orientation and @max determine
274 * from which direction to approach (horizontal
275 * + max = right, vertical + !max = top, etc).
276 * @op_pos, @op_span determine the rows/columns
277 * in which the touching has to happen.
278 */
279static int
280find_attach_position (GtkGrid *grid,
281 GtkOrientation orientation,
282 int op_pos,
283 int op_span,
284 gboolean max)
285{
286 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
287 GtkWidget *child;
288 gboolean hit;
289 int pos;
290
291 if (max)
292 pos = -G_MAXINT;
293 else
294 pos = G_MAXINT;
295
296 hit = FALSE;
297
298 for (child = gtk_widget_get_first_child (GTK_WIDGET (grid));
299 child != NULL;
300 child = gtk_widget_get_next_sibling (widget: child))
301 {
302 GtkGridLayoutChild *grid_child;
303 int attach_pos = 0, attach_span = 0;
304 int opposite_pos = 0, opposite_span = 0;
305
306 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
307
308 switch (orientation)
309 {
310 case GTK_ORIENTATION_HORIZONTAL:
311 attach_pos = gtk_grid_layout_child_get_column (child: grid_child);
312 attach_span = gtk_grid_layout_child_get_column_span (child: grid_child);
313 opposite_pos = gtk_grid_layout_child_get_row (child: grid_child);
314 opposite_span = gtk_grid_layout_child_get_row_span (child: grid_child);
315 break;
316
317 case GTK_ORIENTATION_VERTICAL:
318 attach_pos = gtk_grid_layout_child_get_row (child: grid_child);
319 attach_span = gtk_grid_layout_child_get_row_span (child: grid_child);
320 opposite_pos = gtk_grid_layout_child_get_column (child: grid_child);
321 opposite_span = gtk_grid_layout_child_get_column_span (child: grid_child);
322 break;
323
324 default:
325 break;
326 }
327
328 /* check if the ranges overlap */
329 if (opposite_pos <= op_pos + op_span && op_pos <= opposite_pos + opposite_span)
330 {
331 hit = TRUE;
332
333 if (max)
334 pos = MAX (pos, attach_pos + attach_span);
335 else
336 pos = MIN (pos, attach_pos);
337 }
338 }
339
340 if (!hit)
341 pos = 0;
342
343 return pos;
344}
345
346static void
347gtk_grid_compute_expand (GtkWidget *widget,
348 gboolean *hexpand_p,
349 gboolean *vexpand_p)
350{
351 GtkWidget *w;
352 gboolean hexpand = FALSE;
353 gboolean vexpand = FALSE;
354
355 for (w = gtk_widget_get_first_child (widget);
356 w != NULL;
357 w = gtk_widget_get_next_sibling (widget: w))
358 {
359 hexpand = hexpand || gtk_widget_compute_expand (widget: w, orientation: GTK_ORIENTATION_HORIZONTAL);
360 vexpand = vexpand || gtk_widget_compute_expand (widget: w, orientation: GTK_ORIENTATION_VERTICAL);
361 }
362
363 *hexpand_p = hexpand;
364 *vexpand_p = vexpand;
365}
366
367static GtkSizeRequestMode
368gtk_grid_get_request_mode (GtkWidget *widget)
369{
370 GtkWidget *w;
371 int wfh = 0, hfw = 0;
372
373 for (w = gtk_widget_get_first_child (widget);
374 w != NULL;
375 w = gtk_widget_get_next_sibling (widget: w))
376 {
377 GtkSizeRequestMode mode = gtk_widget_get_request_mode (widget: w);
378
379 switch (mode)
380 {
381 case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
382 hfw ++;
383 break;
384 case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
385 wfh ++;
386 break;
387 case GTK_SIZE_REQUEST_CONSTANT_SIZE:
388 default:
389 break;
390 }
391 }
392
393 if (hfw == 0 && wfh == 0)
394 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
395 else
396 return wfh > hfw ?
397 GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
398 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
399}
400
401static void
402gtk_grid_dispose (GObject *object)
403{
404 GtkWidget *child;
405
406 while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
407 gtk_grid_remove (GTK_GRID (object), child);
408
409 G_OBJECT_CLASS (gtk_grid_parent_class)->dispose (object);
410}
411
412static void
413gtk_grid_class_init (GtkGridClass *class)
414{
415 GObjectClass *object_class = G_OBJECT_CLASS (class);
416 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
417
418 object_class->dispose = gtk_grid_dispose;
419 object_class->get_property = gtk_grid_get_property;
420 object_class->set_property = gtk_grid_set_property;
421
422 widget_class->compute_expand = gtk_grid_compute_expand;
423 widget_class->get_request_mode = gtk_grid_get_request_mode;
424
425 g_object_class_override_property (oclass: object_class, property_id: PROP_ORIENTATION, name: "orientation");
426
427 /**
428 * GtkGrid:row-spacing: (attributes org.gtk.Property.get=gtk_grid_get_row_spacing org.gtk.Property.set=gtk_grid_set_row_spacing)
429 *
430 * The amount of space between two consecutive rows.
431 */
432 obj_properties[PROP_ROW_SPACING] =
433 g_param_spec_int (name: "row-spacing",
434 P_("Row spacing"),
435 P_("The amount of space between two consecutive rows"),
436 minimum: 0, G_MAXINT16, default_value: 0,
437 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
438
439 /**
440 * GtkGrid:column-spacing: (attributes org.gtk.Property.get=gtk_grid_get_column_spacing org.gtk.Property.set=gtk_grid_set_column_spacing)
441 *
442 * The amount of space between two consecutive columns.
443 */
444 obj_properties[PROP_COLUMN_SPACING] =
445 g_param_spec_int (name: "column-spacing",
446 P_("Column spacing"),
447 P_("The amount of space between two consecutive columns"),
448 minimum: 0, G_MAXINT16, default_value: 0,
449 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
450
451 /**
452 * GtkGrid:row-homogeneous: (attributes org.gtk.Property.get=gtk_grid_get_row_homogeneous org.gtk.Property.set=gtk_grid_set_row_homogeneous)
453 *
454 * If %TRUE, the rows are all the same height.
455 */
456 obj_properties[PROP_ROW_HOMOGENEOUS] =
457 g_param_spec_boolean (name: "row-homogeneous",
458 P_("Row Homogeneous"),
459 P_("If TRUE, the rows are all the same height"),
460 FALSE,
461 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
462
463 /**
464 * GtkGrid:column-homogeneous: (attributes org.gtk.Property.get=gtk_grid_get_column_homogeneous org.gtk.Property.set=gtk_grid_set_column_homogeneous)
465 *
466 * If %TRUE, the columns are all the same width.
467 */
468 obj_properties[PROP_COLUMN_HOMOGENEOUS] =
469 g_param_spec_boolean (name: "column-homogeneous",
470 P_("Column Homogeneous"),
471 P_("If TRUE, the columns are all the same width"),
472 FALSE,
473 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
474
475 /**
476 * GtkGrid:baseline-row: (attributes org.gtk.Property.get=gtk_grid_get_baseline_row org.gtk.Property.set=gtk_grid_set_baseline_row)
477 *
478 * The row to align to the baseline when valign is %GTK_ALIGN_BASELINE.
479 */
480 obj_properties[PROP_BASELINE_ROW] =
481 g_param_spec_int (name: "baseline-row",
482 P_("Baseline Row"),
483 P_("The row to align the to the baseline when valign is GTK_ALIGN_BASELINE"),
484 minimum: 0, G_MAXINT, default_value: 0,
485 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
486
487 g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPERTIES, pspecs: obj_properties);
488
489 gtk_widget_class_set_css_name (widget_class, I_("grid"));
490 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_GRID_LAYOUT);
491 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP);
492}
493
494static GtkBuildableIface *parent_buildable_iface;
495
496static void
497gtk_grid_buildable_add_child (GtkBuildable *buildable,
498 GtkBuilder *builder,
499 GObject *child,
500 const char *type)
501{
502 if (GTK_IS_WIDGET (child))
503 {
504 GtkGrid *grid = GTK_GRID ( buildable);
505 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
506 int pos[2] = { 0, 0 };
507
508 pos[priv->orientation] = find_attach_position (grid, orientation: priv->orientation, op_pos: 0, op_span: 1, TRUE);
509 grid_attach (grid, GTK_WIDGET (child), column: pos[0], row: pos[1], width: 1, height: 1);
510 }
511 else
512 parent_buildable_iface->add_child (buildable, builder, child, type);
513}
514
515static void
516gtk_grid_buildable_iface_init (GtkBuildableIface *iface)
517{
518 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
519
520 iface->add_child = gtk_grid_buildable_add_child;
521}
522
523static void
524gtk_grid_init (GtkGrid *grid)
525{
526 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
527
528 priv->layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (grid));
529
530 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
531 gtk_widget_update_orientation (GTK_WIDGET (grid), orientation: priv->orientation);
532}
533
534/**
535 * gtk_grid_new:
536 *
537 * Creates a new grid widget.
538 *
539 * Returns: the new `GtkGrid`
540 */
541GtkWidget *
542gtk_grid_new (void)
543{
544 return g_object_new (GTK_TYPE_GRID, NULL);
545}
546
547/**
548 * gtk_grid_attach:
549 * @grid: a `GtkGrid`
550 * @child: the widget to add
551 * @column: the column number to attach the left side of @child to
552 * @row: the row number to attach the top side of @child to
553 * @width: the number of columns that @child will span
554 * @height: the number of rows that @child will span
555 *
556 * Adds a widget to the grid.
557 *
558 * The position of @child is determined by @column and @row.
559 * The number of “cells” that @child will occupy is determined
560 * by @width and @height.
561 */
562void
563gtk_grid_attach (GtkGrid *grid,
564 GtkWidget *child,
565 int column,
566 int row,
567 int width,
568 int height)
569{
570 g_return_if_fail (GTK_IS_GRID (grid));
571 g_return_if_fail (GTK_IS_WIDGET (child));
572 g_return_if_fail (_gtk_widget_get_parent (child) == NULL);
573 g_return_if_fail (width > 0);
574 g_return_if_fail (height > 0);
575
576 grid_attach (grid, widget: child, column, row, width, height);
577}
578
579/**
580 * gtk_grid_attach_next_to:
581 * @grid: a `GtkGrid`
582 * @child: the widget to add
583 * @sibling: (nullable): the child of @grid that @child will be placed
584 * next to, or %NULL to place @child at the beginning or end
585 * @side: the side of @sibling that @child is positioned next to
586 * @width: the number of columns that @child will span
587 * @height: the number of rows that @child will span
588 *
589 * Adds a widget to the grid.
590 *
591 * The widget is placed next to @sibling, on the side determined by
592 * @side. When @sibling is %NULL, the widget is placed in row (for
593 * left or right placement) or column 0 (for top or bottom placement),
594 * at the end indicated by @side.
595 *
596 * Attaching widgets labeled `[1]`, `[2]`, `[3]` with `@sibling == %NULL` and
597 * `@side == %GTK_POS_LEFT` yields a layout of `[3][2][1]`.
598 */
599void
600gtk_grid_attach_next_to (GtkGrid *grid,
601 GtkWidget *child,
602 GtkWidget *sibling,
603 GtkPositionType side,
604 int width,
605 int height)
606{
607 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
608 GtkGridLayoutChild *grid_sibling;
609 int left, top;
610
611 g_return_if_fail (GTK_IS_GRID (grid));
612 g_return_if_fail (GTK_IS_WIDGET (child));
613 g_return_if_fail (_gtk_widget_get_parent (child) == NULL);
614 g_return_if_fail (sibling == NULL || _gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
615 g_return_if_fail (width > 0);
616 g_return_if_fail (height > 0);
617
618 if (sibling != NULL)
619 {
620 grid_sibling = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child: sibling));
621
622 switch (side)
623 {
624 case GTK_POS_LEFT:
625 left = gtk_grid_layout_child_get_column (child: grid_sibling) - width;
626 top = gtk_grid_layout_child_get_row (child: grid_sibling);
627 break;
628 case GTK_POS_RIGHT:
629 left = gtk_grid_layout_child_get_column (child: grid_sibling) +
630 gtk_grid_layout_child_get_column_span (child: grid_sibling);
631 top = gtk_grid_layout_child_get_row (child: grid_sibling);
632 break;
633 case GTK_POS_TOP:
634 left = gtk_grid_layout_child_get_column (child: grid_sibling);
635 top = gtk_grid_layout_child_get_row (child: grid_sibling) - height;
636 break;
637 case GTK_POS_BOTTOM:
638 left = gtk_grid_layout_child_get_column (child: grid_sibling);
639 top = gtk_grid_layout_child_get_row (child: grid_sibling) +
640 gtk_grid_layout_child_get_row_span (child: grid_sibling);
641 break;
642 default:
643 g_assert_not_reached ();
644 }
645 }
646 else
647 {
648 switch (side)
649 {
650 case GTK_POS_LEFT:
651 left = find_attach_position (grid, orientation: GTK_ORIENTATION_HORIZONTAL, op_pos: 0, op_span: height, FALSE);
652 left -= width;
653 top = 0;
654 break;
655 case GTK_POS_RIGHT:
656 left = find_attach_position (grid, orientation: GTK_ORIENTATION_HORIZONTAL, op_pos: 0, op_span: height, TRUE);
657 top = 0;
658 break;
659 case GTK_POS_TOP:
660 left = 0;
661 top = find_attach_position (grid, orientation: GTK_ORIENTATION_VERTICAL, op_pos: 0, op_span: width, FALSE);
662 top -= height;
663 break;
664 case GTK_POS_BOTTOM:
665 left = 0;
666 top = find_attach_position (grid, orientation: GTK_ORIENTATION_VERTICAL, op_pos: 0, op_span: width, TRUE);
667 break;
668 default:
669 g_assert_not_reached ();
670 }
671 }
672
673 grid_attach (grid, widget: child, column: left, row: top, width, height);
674}
675
676/**
677 * gtk_grid_get_child_at:
678 * @grid: a `GtkGrid`
679 * @column: the left edge of the cell
680 * @row: the top edge of the cell
681 *
682 * Gets the child of @grid whose area covers the grid
683 * cell at @column, @row.
684 *
685 * Returns: (transfer none) (nullable): the child at the given position
686 */
687GtkWidget *
688gtk_grid_get_child_at (GtkGrid *grid,
689 int column,
690 int row)
691{
692 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
693 GtkWidget *child;
694
695 g_return_val_if_fail (GTK_IS_GRID (grid), NULL);
696
697 for (child = gtk_widget_get_first_child (GTK_WIDGET (grid));
698 child != NULL;
699 child = gtk_widget_get_next_sibling (widget: child))
700 {
701 GtkGridLayoutChild *grid_child;
702 int child_column, child_row, child_width, child_height;
703
704 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
705 child_column = gtk_grid_layout_child_get_column (child: grid_child);
706 child_row = gtk_grid_layout_child_get_row (child: grid_child);
707 child_width = gtk_grid_layout_child_get_column_span (child: grid_child);
708 child_height = gtk_grid_layout_child_get_row_span (child: grid_child);
709
710 if (child_column <= column && child_column + child_width > column &&
711 child_row <= row && child_row + child_height > row)
712 return child;
713 }
714
715 return NULL;
716}
717
718/**
719 * gtk_grid_remove:
720 * @grid: a `GtkGrid`
721 * @child: the child widget to remove
722 *
723 * Removes a child from @grid.
724 *
725 * The child must have been added with
726 * [method@Gtk.Grid.attach] or [method@Gtk.Grid.attach_next_to].
727 */
728void
729gtk_grid_remove (GtkGrid *grid,
730 GtkWidget *child)
731{
732 g_return_if_fail (GTK_IS_GRID (grid));
733 g_return_if_fail (GTK_IS_WIDGET (child));
734 g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (grid));
735
736 gtk_widget_unparent (widget: child);
737}
738
739/**
740 * gtk_grid_insert_row:
741 * @grid: a `GtkGrid`
742 * @position: the position to insert the row at
743 *
744 * Inserts a row at the specified position.
745 *
746 * Children which are attached at or below this position
747 * are moved one row down. Children which span across this
748 * position are grown to span the new row.
749 */
750void
751gtk_grid_insert_row (GtkGrid *grid,
752 int position)
753{
754 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
755 GtkWidget *child;
756 int top, height;
757
758 g_return_if_fail (GTK_IS_GRID (grid));
759
760 for (child = gtk_widget_get_first_child (GTK_WIDGET (grid));
761 child != NULL;
762 child = gtk_widget_get_next_sibling (widget: child))
763 {
764 GtkGridLayoutChild *grid_child;
765
766 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
767 top = gtk_grid_layout_child_get_row (child: grid_child);
768 height = gtk_grid_layout_child_get_row_span (child: grid_child);
769
770 if (top >= position)
771 gtk_grid_layout_child_set_row (child: grid_child, row: top + 1);
772 else if (top + height > position)
773 gtk_grid_layout_child_set_row_span (child: grid_child, span: height + 1);
774 }
775}
776
777/**
778 * gtk_grid_remove_row:
779 * @grid: a `GtkGrid`
780 * @position: the position of the row to remove
781 *
782 * Removes a row from the grid.
783 *
784 * Children that are placed in this row are removed,
785 * spanning children that overlap this row have their
786 * height reduced by one, and children below the row
787 * are moved up.
788 */
789void
790gtk_grid_remove_row (GtkGrid *grid,
791 int position)
792{
793 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
794 GtkWidget *child;
795
796 g_return_if_fail (GTK_IS_GRID (grid));
797
798 child = gtk_widget_get_first_child (GTK_WIDGET (grid));
799 while (child)
800 {
801 GtkWidget *next = gtk_widget_get_next_sibling (widget: child);
802 GtkGridLayoutChild *grid_child;
803 int top, height;
804
805 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
806 top = gtk_grid_layout_child_get_row (child: grid_child);
807 height = gtk_grid_layout_child_get_row_span (child: grid_child);
808
809 if (top <= position && top + height > position)
810 height--;
811 if (top > position)
812 top--;
813
814 if (height <= 0)
815 {
816 gtk_grid_remove (grid, child);
817 }
818 else
819 {
820 gtk_grid_layout_child_set_row_span (child: grid_child, span: height);
821 gtk_grid_layout_child_set_row (child: grid_child, row: top);
822 }
823
824 child = next;
825 }
826}
827
828/**
829 * gtk_grid_insert_column:
830 * @grid: a `GtkGrid`
831 * @position: the position to insert the column at
832 *
833 * Inserts a column at the specified position.
834 *
835 * Children which are attached at or to the right of this position
836 * are moved one column to the right. Children which span across this
837 * position are grown to span the new column.
838 */
839void
840gtk_grid_insert_column (GtkGrid *grid,
841 int position)
842{
843 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
844 GtkWidget *child;
845
846 g_return_if_fail (GTK_IS_GRID (grid));
847
848 for (child = gtk_widget_get_first_child (GTK_WIDGET (grid));
849 child != NULL;
850 child = gtk_widget_get_next_sibling (widget: child))
851 {
852 GtkGridLayoutChild *grid_child;
853 int left, width;
854
855 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
856 left = gtk_grid_layout_child_get_column (child: grid_child);
857 width = gtk_grid_layout_child_get_column_span (child: grid_child);
858
859 if (left >= position)
860 gtk_grid_layout_child_set_column (child: grid_child, column: left + 1);
861 else if (left + width > position)
862 gtk_grid_layout_child_set_column_span (child: grid_child, span: width + 1);
863 }
864}
865
866/**
867 * gtk_grid_remove_column:
868 * @grid: a `GtkGrid`
869 * @position: the position of the column to remove
870 *
871 * Removes a column from the grid.
872 *
873 * Children that are placed in this column are removed,
874 * spanning children that overlap this column have their
875 * width reduced by one, and children after the column
876 * are moved to the left.
877 */
878void
879gtk_grid_remove_column (GtkGrid *grid,
880 int position)
881{
882 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
883 GtkWidget *child;
884
885 g_return_if_fail (GTK_IS_GRID (grid));
886
887 child = gtk_widget_get_first_child (GTK_WIDGET (grid));
888 while (child)
889 {
890 GtkWidget *next = gtk_widget_get_next_sibling (widget: child);
891 GtkGridLayoutChild *grid_child;
892 int left, width;
893
894 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
895
896 left = gtk_grid_layout_child_get_column (child: grid_child);
897 width = gtk_grid_layout_child_get_column_span (child: grid_child);
898
899 if (left <= position && left + width > position)
900 width--;
901 if (left > position)
902 left--;
903
904 if (width <= 0)
905 {
906 gtk_grid_remove (grid, child);
907 }
908 else
909 {
910 gtk_grid_layout_child_set_column_span (child: grid_child, span: width);
911 gtk_grid_layout_child_set_column (child: grid_child, column: left);
912 }
913
914 child = next;
915 }
916}
917
918/**
919 * gtk_grid_insert_next_to:
920 * @grid: a `GtkGrid`
921 * @sibling: the child of @grid that the new row or column will be
922 * placed next to
923 * @side: the side of @sibling that @child is positioned next to
924 *
925 * Inserts a row or column at the specified position.
926 *
927 * The new row or column is placed next to @sibling, on the side
928 * determined by @side. If @side is %GTK_POS_TOP or %GTK_POS_BOTTOM,
929 * a row is inserted. If @side is %GTK_POS_LEFT of %GTK_POS_RIGHT,
930 * a column is inserted.
931 */
932void
933gtk_grid_insert_next_to (GtkGrid *grid,
934 GtkWidget *sibling,
935 GtkPositionType side)
936{
937 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
938 GtkGridLayoutChild *child;
939
940 g_return_if_fail (GTK_IS_GRID (grid));
941 g_return_if_fail (GTK_IS_WIDGET (sibling));
942 g_return_if_fail (_gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
943
944 child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child: sibling));
945
946 switch (side)
947 {
948 case GTK_POS_LEFT:
949 gtk_grid_insert_column (grid, position: gtk_grid_layout_child_get_column (child));
950 break;
951 case GTK_POS_RIGHT:
952 {
953 int col = gtk_grid_layout_child_get_column (child) +
954 gtk_grid_layout_child_get_column_span (child);
955 gtk_grid_insert_column (grid, position: col);
956 }
957 break;
958 case GTK_POS_TOP:
959 gtk_grid_insert_row (grid, position: gtk_grid_layout_child_get_row (child));
960 break;
961 case GTK_POS_BOTTOM:
962 {
963 int row = gtk_grid_layout_child_get_row (child) +
964 gtk_grid_layout_child_get_row_span (child);
965 gtk_grid_insert_row (grid, position: row);
966 }
967 break;
968 default:
969 g_assert_not_reached ();
970 }
971}
972
973/**
974 * gtk_grid_set_row_homogeneous: (attributes org.gtk.Method.set_property=row-homogeneous)
975 * @grid: a `GtkGrid`
976 * @homogeneous: %TRUE to make rows homogeneous
977 *
978 * Sets whether all rows of @grid will have the same height.
979 */
980void
981gtk_grid_set_row_homogeneous (GtkGrid *grid,
982 gboolean homogeneous)
983{
984 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
985 gboolean old_val;
986
987 g_return_if_fail (GTK_IS_GRID (grid));
988
989 old_val = gtk_grid_layout_get_row_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
990 if (old_val != !!homogeneous)
991 {
992 gtk_grid_layout_set_row_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager), homogeneous);
993 g_object_notify_by_pspec (G_OBJECT (grid), pspec: obj_properties [PROP_ROW_HOMOGENEOUS]);
994 }
995}
996
997/**
998 * gtk_grid_get_row_homogeneous: (attributes org.gtk.Method.get_property=row-homogeneous)
999 * @grid: a `GtkGrid`
1000 *
1001 * Returns whether all rows of @grid have the same height.
1002 *
1003 * Returns: whether all rows of @grid have the same height.
1004 */
1005gboolean
1006gtk_grid_get_row_homogeneous (GtkGrid *grid)
1007{
1008 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1009
1010 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1011
1012 return gtk_grid_layout_get_row_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1013}
1014
1015/**
1016 * gtk_grid_set_column_homogeneous: (attributes org.gtk.Method.set_property=column-homogeneous)
1017 * @grid: a `GtkGrid`
1018 * @homogeneous: %TRUE to make columns homogeneous
1019 *
1020 * Sets whether all columns of @grid will have the same width.
1021 */
1022void
1023gtk_grid_set_column_homogeneous (GtkGrid *grid,
1024 gboolean homogeneous)
1025{
1026 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1027 gboolean old_val;
1028
1029 g_return_if_fail (GTK_IS_GRID (grid));
1030
1031 old_val = gtk_grid_layout_get_column_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1032 if (old_val != !!homogeneous)
1033 {
1034 gtk_grid_layout_set_column_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager), homogeneous);
1035 g_object_notify_by_pspec (G_OBJECT (grid), pspec: obj_properties [PROP_COLUMN_HOMOGENEOUS]);
1036 }
1037}
1038
1039/**
1040 * gtk_grid_get_column_homogeneous: (attributes org.gtk.Method.get_property=column-homogeneous)
1041 * @grid: a `GtkGrid`
1042 *
1043 * Returns whether all columns of @grid have the same width.
1044 *
1045 * Returns: whether all columns of @grid have the same width.
1046 */
1047gboolean
1048gtk_grid_get_column_homogeneous (GtkGrid *grid)
1049{
1050 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1051
1052 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
1053
1054 return gtk_grid_layout_get_column_homogeneous (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1055}
1056
1057/**
1058 * gtk_grid_set_row_spacing: (attributes org.gtk.Method.set_property=row-spacing)
1059 * @grid: a `GtkGrid`
1060 * @spacing: the amount of space to insert between rows
1061 *
1062 * Sets the amount of space between rows of @grid.
1063 */
1064void
1065gtk_grid_set_row_spacing (GtkGrid *grid,
1066 guint spacing)
1067{
1068 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1069 guint old_spacing;
1070
1071 g_return_if_fail (GTK_IS_GRID (grid));
1072 g_return_if_fail (spacing <= G_MAXINT16);
1073
1074 old_spacing = gtk_grid_layout_get_row_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1075 if (old_spacing != spacing)
1076 {
1077 gtk_grid_layout_set_row_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager), spacing);
1078 g_object_notify_by_pspec (G_OBJECT (grid), pspec: obj_properties [PROP_ROW_SPACING]);
1079 }
1080}
1081
1082/**
1083 * gtk_grid_get_row_spacing: (attributes org.gtk.Method.get_property=row-spacing)
1084 * @grid: a `GtkGrid`
1085 *
1086 * Returns the amount of space between the rows of @grid.
1087 *
1088 * Returns: the row spacing of @grid
1089 */
1090guint
1091gtk_grid_get_row_spacing (GtkGrid *grid)
1092{
1093 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1094
1095 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1096
1097 return gtk_grid_layout_get_row_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1098}
1099
1100/**
1101 * gtk_grid_set_column_spacing: (attributes org.gtk.Method.set_property=column-spacing)
1102 * @grid: a `GtkGrid`
1103 * @spacing: the amount of space to insert between columns
1104 *
1105 * Sets the amount of space between columns of @grid.
1106 */
1107void
1108gtk_grid_set_column_spacing (GtkGrid *grid,
1109 guint spacing)
1110{
1111 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1112 guint old_spacing;
1113
1114 g_return_if_fail (GTK_IS_GRID (grid));
1115 g_return_if_fail (spacing <= G_MAXINT16);
1116
1117 old_spacing = gtk_grid_layout_get_column_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1118 if (old_spacing != spacing)
1119 {
1120 gtk_grid_layout_set_column_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager), spacing);
1121 g_object_notify_by_pspec (G_OBJECT (grid), pspec: obj_properties [PROP_COLUMN_SPACING]);
1122 }
1123}
1124
1125/**
1126 * gtk_grid_get_column_spacing: (attributes org.gtk.Method.get_property=column-spacing)
1127 * @grid: a `GtkGrid`
1128 *
1129 * Returns the amount of space between the columns of @grid.
1130 *
1131 * Returns: the column spacing of @grid
1132 */
1133guint
1134gtk_grid_get_column_spacing (GtkGrid *grid)
1135{
1136 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1137
1138 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1139
1140 return gtk_grid_layout_get_column_spacing (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1141}
1142
1143/**
1144 * gtk_grid_set_row_baseline_position:
1145 * @grid: a `GtkGrid`
1146 * @row: a row index
1147 * @pos: a `GtkBaselinePosition`
1148 *
1149 * Sets how the baseline should be positioned on @row of the
1150 * grid, in case that row is assigned more space than is requested.
1151 *
1152 * The default baseline position is %GTK_BASELINE_POSITION_CENTER.
1153 */
1154void
1155gtk_grid_set_row_baseline_position (GtkGrid *grid,
1156 int row,
1157 GtkBaselinePosition pos)
1158{
1159 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1160
1161 g_return_if_fail (GTK_IS_GRID (grid));
1162
1163 gtk_grid_layout_set_row_baseline_position (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager),
1164 row,
1165 pos);
1166}
1167
1168/**
1169 * gtk_grid_get_row_baseline_position:
1170 * @grid: a `GtkGrid`
1171 * @row: a row index
1172 *
1173 * Returns the baseline position of @row.
1174 *
1175 * See [method@Gtk.Grid.set_row_baseline_position].
1176 *
1177 * Returns: the baseline position of @row
1178 */
1179GtkBaselinePosition
1180gtk_grid_get_row_baseline_position (GtkGrid *grid,
1181 int row)
1182{
1183 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1184
1185 g_return_val_if_fail (GTK_IS_GRID (grid), GTK_BASELINE_POSITION_CENTER);
1186
1187 return gtk_grid_layout_get_row_baseline_position (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager), row);
1188}
1189
1190/**
1191 * gtk_grid_set_baseline_row: (attributes org.gtk.Method.set_property=baseline-row)
1192 * @grid: a `GtkGrid`
1193 * @row: the row index
1194 *
1195 * Sets which row defines the global baseline for the entire grid.
1196 *
1197 * Each row in the grid can have its own local baseline, but only
1198 * one of those is global, meaning it will be the baseline in the
1199 * parent of the @grid.
1200 */
1201void
1202gtk_grid_set_baseline_row (GtkGrid *grid,
1203 int row)
1204{
1205 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1206 int old_row;
1207
1208 g_return_if_fail (GTK_IS_GRID (grid));
1209
1210 old_row = gtk_grid_layout_get_baseline_row (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1211 if (old_row != row)
1212 {
1213 gtk_grid_layout_set_baseline_row (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager), row);
1214 g_object_notify (G_OBJECT (grid), property_name: "baseline-row");
1215 }
1216}
1217
1218/**
1219 * gtk_grid_get_baseline_row: (attributes org.gtk.Method.get_property=baseline-row)
1220 * @grid: a `GtkGrid`
1221 *
1222 * Returns which row defines the global baseline of @grid.
1223 *
1224 * Returns: the row index defining the global baseline
1225 */
1226int
1227gtk_grid_get_baseline_row (GtkGrid *grid)
1228{
1229 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1230
1231 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
1232
1233 return gtk_grid_layout_get_baseline_row (grid: GTK_GRID_LAYOUT (ptr: priv->layout_manager));
1234}
1235
1236/**
1237 * gtk_grid_query_child:
1238 * @grid: a `GtkGrid`
1239 * @child: a `GtkWidget` child of @grid
1240 * @column: (out) (optional): the column used to attach the left side of @child
1241 * @row: (out) (optional): the row used to attach the top side of @child
1242 * @width: (out) (optional): the number of columns @child spans
1243 * @height: (out) (optional): the number of rows @child spans
1244 *
1245 * Queries the attach points and spans of @child inside the given `GtkGrid`.
1246 */
1247void
1248gtk_grid_query_child (GtkGrid *grid,
1249 GtkWidget *child,
1250 int *column,
1251 int *row,
1252 int *width,
1253 int *height)
1254{
1255 GtkGridPrivate *priv = gtk_grid_get_instance_private (self: grid);
1256 GtkGridLayoutChild *grid_child;
1257
1258 g_return_if_fail (GTK_IS_GRID (grid));
1259 g_return_if_fail (GTK_IS_WIDGET (child));
1260 g_return_if_fail (_gtk_widget_get_parent (child) == (GtkWidget *) grid);
1261
1262 grid_child = GTK_GRID_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout_manager, child));
1263
1264 if (column != NULL)
1265 *column = gtk_grid_layout_child_get_column (child: grid_child);
1266 if (row != NULL)
1267 *row = gtk_grid_layout_child_get_row (child: grid_child);
1268 if (width != NULL)
1269 *width = gtk_grid_layout_child_get_column_span (child: grid_child);
1270 if (height != NULL)
1271 *height = gtk_grid_layout_child_get_row_span (child: grid_child);
1272}
1273

source code of gtk/gtk/gtkgrid.c