gtk2/gtk/gtkgridlayout.c
Sophie Herold a546ae32d7 Remove all nicks and blurbs from param specs
Those property features don't seem to be in use anywhere.
They are redundant since the docs cover the same information
and more. They also created unnecessary translation work.

Closes #4904
2022-05-11 18:16:29 +02:00

2035 lines
58 KiB
C

/* gtkgridlayout.c: Layout manager for grid-like widgets
* Copyright 2019 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* GtkGridLayout:
*
* `GtkGridLayout` is a layout manager which arranges child widgets in
* rows and columns.
*
* Children have an "attach point" defined by the horizontal and vertical
* index of the cell they occupy; children can span multiple rows or columns.
* The layout properties for setting the attach points and spans are set
* using the [class@Gtk.GridLayoutChild] associated to each child widget.
*
* The behaviour of `GtkGridLayout` when several children occupy the same
* grid cell is undefined.
*
* `GtkGridLayout` can be used like a `GtkBoxLayout` if all children are
* attached to the same row or column; however, if you only ever need a
* single row or column, you should consider using `GtkBoxLayout`.
*/
/**
* GtkGridLayoutChild:
*
* `GtkLayoutChild` subclass for children in a `GtkGridLayout`.
*/
#include "config.h"
#include "gtkgridlayout.h"
#include "gtkcsspositionvalueprivate.h"
#include "gtkdebug.h"
#include "gtkintl.h"
#include "gtklayoutchild.h"
#include "gtkorientable.h"
#include "gtkprivate.h"
#include "gtksizerequest.h"
#include "gtkwidgetprivate.h"
#include "gtkcssnodeprivate.h"
/* {{{ GtkGridLayoutChild */
typedef struct {
int pos;
int span;
} GridChildAttach;
struct _GtkGridLayoutChild
{
GtkLayoutChild parent_instance;
GridChildAttach attach[2];
};
#define CHILD_COLUMN(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
#define CHILD_COL_SPAN(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
#define CHILD_ROW(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
#define CHILD_ROW_SPAN(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
enum {
PROP_CHILD_COLUMN = 1,
PROP_CHILD_ROW,
PROP_CHILD_COLUMN_SPAN,
PROP_CHILD_ROW_SPAN,
N_CHILD_PROPERTIES
};
static GParamSpec *child_props[N_CHILD_PROPERTIES];
G_DEFINE_TYPE (GtkGridLayoutChild, gtk_grid_layout_child, GTK_TYPE_LAYOUT_CHILD)
static void
gtk_grid_layout_child_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkGridLayoutChild *self = GTK_GRID_LAYOUT_CHILD (gobject);
switch (prop_id)
{
case PROP_CHILD_COLUMN:
gtk_grid_layout_child_set_column (self, g_value_get_int (value));
break;
case PROP_CHILD_ROW :
gtk_grid_layout_child_set_row (self, g_value_get_int (value));
break;
case PROP_CHILD_COLUMN_SPAN:
gtk_grid_layout_child_set_column_span (self, g_value_get_int (value));
break;
case PROP_CHILD_ROW_SPAN:
gtk_grid_layout_child_set_row_span (self, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_grid_layout_child_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkGridLayoutChild *self = GTK_GRID_LAYOUT_CHILD (gobject);
switch (prop_id)
{
case PROP_CHILD_COLUMN:
g_value_set_int (value, CHILD_COLUMN (self));
break;
case PROP_CHILD_ROW:
g_value_set_int (value, CHILD_ROW (self));
break;
case PROP_CHILD_COLUMN_SPAN:
g_value_set_int (value, CHILD_COL_SPAN (self));
break;
case PROP_CHILD_ROW_SPAN:
g_value_set_int (value, CHILD_ROW_SPAN (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_grid_layout_child_class_init (GtkGridLayoutChildClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gtk_grid_layout_child_set_property;
gobject_class->get_property = gtk_grid_layout_child_get_property;
/**
* GtkGridLayoutChild:column: (attributes org.gtk.Property.get=gtk_grid_layout_child_get_column org.gtk.Property.set=gtk_grid_layout_child_set_column)
*
* The column to place the child in.
*/
child_props[PROP_CHILD_COLUMN] =
g_param_spec_int ("column", NULL, NULL,
G_MININT, G_MAXINT, 0,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayoutChild:row: (attributes org.gtk.Property.get=gtk_grid_layout_child_get_row org.gtk.Property.set=gtk_grid_layout_child_set_row)
*
* The row to place the child in.
*/
child_props[PROP_CHILD_ROW] =
g_param_spec_int ("row", NULL, NULL,
G_MININT, G_MAXINT, 0,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayoutChild:column-span: (attributes org.gtk.Property.get=gtk_grid_layout_child_get_column_span org.gtk.Property.set=gtk_grid_layout_child_set_column_span)
*
* The number of columns the child spans to.
*/
child_props[PROP_CHILD_COLUMN_SPAN] =
g_param_spec_int ("column-span", NULL, NULL,
1, G_MAXINT, 1,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayoutChild:row-span: (attributes org.gtk.Property.get=gtk_grid_layout_child_get_row_span org.gtk.Property.set=gtk_grid_layout_child_set_row_span)
*
* The number of rows the child spans to.
*/
child_props[PROP_CHILD_ROW_SPAN] =
g_param_spec_int ("row-span", NULL, NULL,
1, G_MAXINT, 1,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_CHILD_PROPERTIES, child_props);
}
static void
gtk_grid_layout_child_init (GtkGridLayoutChild *self)
{
CHILD_ROW_SPAN (self) = 1;
CHILD_COL_SPAN (self) = 1;
}
/**
* gtk_grid_layout_child_set_row: (attributes org.gtk.Method.set_property=row)
* @child: a `GtkGridLayoutChild`
* @row: the row for @child
*
* Sets the row to place @child in.
*/
void
gtk_grid_layout_child_set_row (GtkGridLayoutChild *child,
int row)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child));
if (CHILD_ROW (child) == row)
return;
CHILD_ROW (child) = row;
gtk_layout_manager_layout_changed (gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child)));
g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_CHILD_ROW]);
}
/**
* gtk_grid_layout_child_get_row: (attributes org.gtk.Method.get_property=row)
* @child: a `GtkGridLayoutChild`
*
* Retrieves the row number to which @child attaches its top side.
*
* Returns: the row number
*/
int
gtk_grid_layout_child_get_row (GtkGridLayoutChild *child)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child), 0);
return CHILD_ROW (child);
}
/**
* gtk_grid_layout_child_set_column: (attributes org.gtk.Method.set_property=column)
* @child: a `GtkGridLayoutChild`
* @column: the attach point for @child
*
* Sets the column number to attach the left side of @child.
*/
void
gtk_grid_layout_child_set_column (GtkGridLayoutChild *child,
int column)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child));
if (CHILD_COLUMN (child) == column)
return;
CHILD_COLUMN (child) = column;
gtk_layout_manager_layout_changed (gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child)));
g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_CHILD_COLUMN]);
}
/**
* gtk_grid_layout_child_get_column: (attributes org.gtk.Method.get_property=column)
* @child: a `GtkGridLayoutChild`
*
* Retrieves the column number to which @child attaches its left side.
*
* Returns: the column number
*/
int
gtk_grid_layout_child_get_column (GtkGridLayoutChild *child)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child), 0);
return CHILD_COLUMN (child);
}
/**
* gtk_grid_layout_child_set_column_span: (attributes org.gtk.Method.set_property=column-span)
* @child: a `GtkGridLayoutChild`
* @span: the span of @child
*
* Sets the number of columns @child spans to.
*/
void
gtk_grid_layout_child_set_column_span (GtkGridLayoutChild *child,
int span)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child));
if (CHILD_COL_SPAN (child) == span)
return;
CHILD_COL_SPAN (child) = span;
gtk_layout_manager_layout_changed (gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child)));
g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_CHILD_COLUMN_SPAN]);
}
/**
* gtk_grid_layout_child_get_column_span: (attributes org.gtk.Method.get_property=column-span)
* @child: a `GtkGridLayoutChild`
*
* Retrieves the number of columns that @child spans to.
*
* Returns: the number of columns
*/
int
gtk_grid_layout_child_get_column_span (GtkGridLayoutChild *child)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child), 1);
return CHILD_COL_SPAN (child);
}
/**
* gtk_grid_layout_child_set_row_span: (attributes org.gtk.Method.set_property=row-span)
* @child: a `GtkGridLayoutChild`
* @span: the span of @child
*
* Sets the number of rows @child spans to.
*/
void
gtk_grid_layout_child_set_row_span (GtkGridLayoutChild *child,
int span)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child));
if (CHILD_ROW_SPAN (child) == span)
return;
CHILD_ROW_SPAN (child) = span;
gtk_layout_manager_layout_changed (gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child)));
g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_CHILD_ROW_SPAN]);
}
/**
* gtk_grid_layout_child_get_row_span: (attributes org.gtk.Method.get_property=row-span)
* @child: a `GtkGridLayoutChild`
*
* Retrieves the number of rows that @child spans to.
*
* Returns: the number of row
*/
int
gtk_grid_layout_child_get_row_span (GtkGridLayoutChild *child)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child), 1);
return CHILD_ROW_SPAN (child);
}
/* }}} */
/* {{{ GtkGridLayout */
typedef struct {
int row;
GtkBaselinePosition baseline_position;
} GridRowProperties;
static const GridRowProperties grid_row_properties_default = {
0,
GTK_BASELINE_POSITION_CENTER
};
/* A GridLineData struct contains row/column specific parts
* of the grid.
*/
typedef struct {
gint16 spacing;
guint homogeneous : 1;
} GridLineData;
#define ROWS(layout) (&(layout)->linedata[GTK_ORIENTATION_HORIZONTAL])
#define COLUMNS(layout) (&(layout)->linedata[GTK_ORIENTATION_VERTICAL])
/* A GridLine struct represents a single row or column
* during size requests
*/
typedef struct {
int minimum;
int natural;
int minimum_above;
int minimum_below;
int natural_above;
int natural_below;
int position;
int allocation;
int allocated_baseline;
guint need_expand : 1;
guint expand : 1;
guint empty : 1;
} GridLine;
typedef struct {
GridLine *lines;
int min, max;
} GridLines;
typedef struct {
GtkGridLayout *layout;
GtkWidget *widget;
GridLines lines[2];
} GridRequest;
struct _GtkGridLayout
{
GtkLayoutManager parent_instance;
/* Array<GridRowProperties> */
GArray *row_properties;
GtkOrientation orientation;
int baseline_row;
GridLineData linedata[2];
};
enum {
PROP_ROW_SPACING = 1,
PROP_COLUMN_SPACING,
PROP_ROW_HOMOGENEOUS,
PROP_COLUMN_HOMOGENEOUS,
PROP_BASELINE_ROW,
N_PROPERTIES
};
static GParamSpec *layout_props[N_PROPERTIES];
G_DEFINE_TYPE (GtkGridLayout, gtk_grid_layout, GTK_TYPE_LAYOUT_MANAGER)
static inline GtkGridLayoutChild *
get_grid_child (GtkGridLayout *self,
GtkWidget *child)
{
GtkLayoutManager *manager = GTK_LAYOUT_MANAGER (self);
return (GtkGridLayoutChild *) gtk_layout_manager_get_layout_child (manager, child);
}
static int
get_spacing (GtkGridLayout *self,
GtkWidget *widget,
GtkOrientation orientation)
{
GtkCssNode *node = gtk_widget_get_css_node (widget);
GtkCssStyle *style = gtk_css_node_get_style (node);
GtkCssValue *border_spacing;
int css_spacing;
border_spacing = style->size->border_spacing;
if (orientation == GTK_ORIENTATION_HORIZONTAL)
css_spacing = _gtk_css_position_value_get_x (border_spacing, 100);
else
css_spacing = _gtk_css_position_value_get_y (border_spacing, 100);
return css_spacing + self->linedata[orientation].spacing;
}
/* Calculates the min and max numbers for both orientations. */
static void
grid_request_count_lines (GridRequest *request)
{
GtkWidget *child;
int min[2];
int max[2];
min[0] = min[1] = G_MAXINT;
max[0] = max[1] = G_MININT;
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
GridChildAttach *attach = grid_child->attach;
min[0] = MIN (min[0], attach[0].pos);
max[0] = MAX (max[0], attach[0].pos + attach[0].span);
min[1] = MIN (min[1], attach[1].pos);
max[1] = MAX (max[1], attach[1].pos + attach[1].span);
}
request->lines[0].min = min[0];
request->lines[0].max = max[0];
request->lines[1].min = min[1];
request->lines[1].max = max[1];
}
/* Sets line sizes to 0 and marks lines as expand
* if they have a non-spanning expanding child.
*/
static void
grid_request_init (GridRequest *request,
GtkOrientation orientation)
{
GtkWidget *child;
GridLines *lines;
int i;
lines = &request->lines[orientation];
for (i = 0; i < lines->max - lines->min; i++)
{
lines->lines[i].minimum = 0;
lines->lines[i].natural = 0;
lines->lines[i].minimum_above = -1;
lines->lines[i].minimum_below = -1;
lines->lines[i].natural_above = -1;
lines->lines[i].natural_below = -1;
lines->lines[i].expand = FALSE;
lines->lines[i].empty = TRUE;
}
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
GridChildAttach *attach;
attach = &grid_child->attach[orientation];
if (attach->span == 1 && gtk_widget_compute_expand (child, orientation))
lines->lines[attach->pos - lines->min].expand = TRUE;
}
}
/* Sums allocations for lines spanned by child and their spacing.
*/
static int
compute_allocation_for_child (GridRequest *request,
GtkGridLayoutChild *child,
GtkOrientation orientation)
{
GridLines *lines;
GridLine *line;
GridChildAttach *attach;
int size;
int i;
lines = &request->lines[orientation];
attach = &child->attach[orientation];
size = (attach->span - 1) * get_spacing (request->layout, request->widget, orientation);
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
size += line->allocation;
}
return size;
}
static void
compute_request_for_child (GridRequest *request,
GtkWidget *child,
GtkGridLayoutChild *grid_child,
GtkOrientation orientation,
gboolean contextual,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
if (minimum_baseline != NULL)
*minimum_baseline = -1;
if (natural_baseline != NULL)
*natural_baseline = -1;
if (contextual)
{
int size;
size = compute_allocation_for_child (request, grid_child, 1 - orientation);
gtk_widget_measure (child,
orientation,
size,
minimum, natural,
minimum_baseline, natural_baseline);
}
else
{
gtk_widget_measure (child,
orientation,
-1,
minimum, natural,
minimum_baseline, natural_baseline);
}
}
/* Sets requisition to max. of non-spanning children.
* If contextual is TRUE, requires allocations of
* lines in the opposite orientation to be set.
*/
static void
grid_request_non_spanning (GridRequest *request,
GtkOrientation orientation,
gboolean contextual)
{
GtkWidget *child;
GridLines *lines;
GridLine *line;
int i;
GtkBaselinePosition baseline_pos;
int minimum, minimum_baseline;
int natural, natural_baseline;
lines = &request->lines[orientation];
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
GridChildAttach *attach;
if (!gtk_widget_should_layout (child))
continue;
attach = &grid_child->attach[orientation];
if (attach->span != 1)
continue;
compute_request_for_child (request, child, grid_child, orientation, contextual, &minimum, &natural, &minimum_baseline, &natural_baseline);
line = &lines->lines[attach->pos - lines->min];
if (minimum_baseline != -1)
{
line->minimum_above = MAX (line->minimum_above, minimum_baseline);
line->minimum_below = MAX (line->minimum_below, minimum - minimum_baseline);
line->natural_above = MAX (line->natural_above, natural_baseline);
line->natural_below = MAX (line->natural_below, natural - natural_baseline);
}
else
{
line->minimum = MAX (line->minimum, minimum);
line->natural = MAX (line->natural, natural);
}
}
for (i = 0; i < lines->max - lines->min; i++)
{
line = &lines->lines[i];
if (line->minimum_above != -1)
{
line->minimum = MAX (line->minimum, line->minimum_above + line->minimum_below);
line->natural = MAX (line->natural, line->natural_above + line->natural_below);
baseline_pos = gtk_grid_layout_get_row_baseline_position (request->layout, i + lines->min);
switch (baseline_pos)
{
case GTK_BASELINE_POSITION_TOP:
line->minimum_above += 0;
line->minimum_below += line->minimum - (line->minimum_above + line->minimum_below);
line->natural_above += 0;
line->natural_below += line->natural - (line->natural_above + line->natural_below);
break;
case GTK_BASELINE_POSITION_CENTER:
line->minimum_above += (line->minimum - (line->minimum_above + line->minimum_below))/2;
line->minimum_below += (line->minimum - (line->minimum_above + line->minimum_below))/2;
line->natural_above += (line->natural - (line->natural_above + line->natural_below))/2;
line->natural_below += (line->natural - (line->natural_above + line->natural_below))/2;
break;
case GTK_BASELINE_POSITION_BOTTOM:
line->minimum_above += line->minimum - (line->minimum_above + line->minimum_below);
line->minimum_below += 0;
line->natural_above += line->natural - (line->natural_above + line->natural_below);
line->natural_below += 0;
break;
default:
break;
}
}
}
}
/* Enforce homogeneous sizes */
static void
grid_request_homogeneous (GridRequest *request,
GtkOrientation orientation)
{
GtkGridLayout *self = request->layout;
GridLineData *linedata;
GridLines *lines;
int minimum, natural;
int i;
linedata = &self->linedata[orientation];
lines = &request->lines[orientation];
if (!linedata->homogeneous)
return;
minimum = 0;
natural = 0;
for (i = 0; i < lines->max - lines->min; i++)
{
minimum = MAX (minimum, lines->lines[i].minimum);
natural = MAX (natural, lines->lines[i].natural);
}
for (i = 0; i < lines->max - lines->min; i++)
{
lines->lines[i].minimum = minimum;
lines->lines[i].natural = natural;
/* TODO: Do we want to adjust the baseline here too?
* And if so, also in the homogeneous resize.
*/
}
}
/* Deals with spanning children.
* Requires expand fields of lines to be set for
* non-spanning children.
*/
static void
grid_request_spanning (GridRequest *request,
GtkOrientation orientation,
gboolean contextual)
{
GtkGridLayout *self = request->layout;
GtkWidget *child;
GridChildAttach *attach;
GridLineData *linedata;
GridLines *lines;
GridLine *line;
int minimum, natural;
int span_minimum, span_natural;
int span_expand;
gboolean force_expand;
int spacing;
int extra;
int expand;
int line_extra;
int i;
linedata = &self->linedata[orientation];
lines = &request->lines[orientation];
spacing = get_spacing (request->layout, request->widget, orientation);
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
if (!gtk_widget_should_layout (child))
continue;
attach = &grid_child->attach[orientation];
if (attach->span == 1)
continue;
/* We ignore baselines for spanning children */
compute_request_for_child (request, child, grid_child, orientation, contextual, &minimum, &natural, NULL, NULL);
span_minimum = (attach->span - 1) * spacing;
span_natural = (attach->span - 1) * spacing;
span_expand = 0;
force_expand = FALSE;
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
span_minimum += line->minimum;
span_natural += line->natural;
if (line->expand)
span_expand += 1;
}
if (span_expand == 0)
{
span_expand = attach->span;
force_expand = TRUE;
}
/* If we need to request more space for this child to fill
* its requisition, then divide up the needed space amongst the
* lines it spans, favoring expandable lines if any.
*
* When doing homogeneous allocation though, try to keep the
* line allocations even, since we're going to force them to
* be the same anyway, and we don't want to introduce unnecessary
* extra space.
*/
if (span_minimum < minimum)
{
if (linedata->homogeneous)
{
int total, m;
total = minimum - (attach->span - 1) * spacing;
m = total / attach->span + (total % attach->span ? 1 : 0);
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
line->minimum = MAX (line->minimum, m);
}
}
else
{
extra = minimum - span_minimum;
expand = span_expand;
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
if (force_expand || line->expand)
{
line_extra = extra / expand;
line->minimum += line_extra;
extra -= line_extra;
expand -= 1;
}
}
}
}
if (span_natural < natural)
{
if (linedata->homogeneous)
{
int total, n;
total = natural - (attach->span - 1) * spacing;
n = total / attach->span + (total % attach->span ? 1 : 0);
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
line->natural = MAX (line->natural, n);
}
}
else
{
extra = natural - span_natural;
expand = span_expand;
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
if (force_expand || line->expand)
{
line_extra = extra / expand;
line->natural += line_extra;
extra -= line_extra;
expand -= 1;
}
}
}
}
}
}
/* Marks empty and expanding lines and counts them */
static void
grid_request_compute_expand (GridRequest *request,
GtkOrientation orientation,
int min,
int max,
int *nonempty_lines,
int *expand_lines)
{
GtkWidget *child;
GridChildAttach *attach;
int i;
GridLines *lines;
GridLine *line;
gboolean has_expand;
int expand;
int empty;
lines = &request->lines[orientation];
min = MAX (min, lines->min);
max = MIN (max, lines->max);
for (i = min - lines->min; i < max - lines->min; i++)
{
lines->lines[i].need_expand = FALSE;
lines->lines[i].expand = FALSE;
lines->lines[i].empty = TRUE;
}
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
if (!gtk_widget_should_layout (child))
continue;
attach = &grid_child->attach[orientation];
if (attach->span != 1)
continue;
if (attach->pos >= max || attach->pos < min)
continue;
line = &lines->lines[attach->pos - lines->min];
line->empty = FALSE;
if (gtk_widget_compute_expand (child, orientation))
line->expand = TRUE;
}
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
if (!gtk_widget_should_layout (child))
continue;
attach = &grid_child->attach[orientation];
if (attach->span == 1)
continue;
has_expand = FALSE;
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
if (line->expand)
has_expand = TRUE;
if (attach->pos + i >= max || attach->pos + 1 < min)
continue;
line->empty = FALSE;
}
if (!has_expand && gtk_widget_compute_expand (child, orientation))
{
for (i = 0; i < attach->span; i++)
{
if (attach->pos + i >= max || attach->pos + 1 < min)
continue;
line = &lines->lines[attach->pos - lines->min + i];
line->need_expand = TRUE;
}
}
}
empty = 0;
expand = 0;
for (i = min - lines->min; i < max - lines->min; i++)
{
line = &lines->lines[i];
if (line->need_expand)
line->expand = TRUE;
if (line->empty)
empty += 1;
if (line->expand)
expand += 1;
}
if (nonempty_lines)
*nonempty_lines = max - min - empty;
if (expand_lines)
*expand_lines = expand;
}
/* Sums the minimum and natural fields of lines and their spacing */
static void
grid_request_sum (GridRequest *request,
GtkOrientation orientation,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkGridLayout *self = request->layout;
GridLines *lines;
int i;
int min, nat;
int nonempty;
int spacing;
grid_request_compute_expand (request, orientation, G_MININT, G_MAXINT, &nonempty, NULL);
lines = &request->lines[orientation];
spacing = get_spacing (request->layout, request->widget, orientation);
min = 0;
nat = 0;
for (i = 0; i < lines->max - lines->min; i++)
{
if (orientation == GTK_ORIENTATION_VERTICAL &&
lines->min + i == self->baseline_row &&
lines->lines[i].minimum_above != -1)
{
if (minimum_baseline)
*minimum_baseline = min + lines->lines[i].minimum_above;
if (natural_baseline)
*natural_baseline = nat + lines->lines[i].natural_above;
}
min += lines->lines[i].minimum;
nat += lines->lines[i].natural;
if (!lines->lines[i].empty)
{
min += spacing;
nat += spacing;
}
}
/* Remove last spacing, if any was applied */
if (nonempty > 0)
{
min -= spacing;
nat -= spacing;
}
*minimum = min;
*natural = nat;
}
/* Computes minimum and natural fields of lines.
* When contextual is TRUE, requires allocation of
* lines in the opposite orientation to be set.
*/
static void
grid_request_run (GridRequest *request,
GtkOrientation orientation,
gboolean contextual)
{
grid_request_init (request, orientation);
grid_request_non_spanning (request, orientation, contextual);
grid_request_homogeneous (request, orientation);
grid_request_spanning (request, orientation, contextual);
grid_request_homogeneous (request, orientation);
}
static void
grid_distribute_non_homogeneous (GridLines *lines,
int nonempty,
int expand,
int size,
int min,
int max)
{
GtkRequestedSize *sizes;
GridLine *line;
int extra;
int rest;
int i, j;
if (nonempty == 0)
return;
sizes = g_newa (GtkRequestedSize, nonempty);
j = 0;
for (i = min - lines->min; i < max - lines->min; i++)
{
line = &lines->lines[i];
if (line->empty)
continue;
size -= line->minimum;
sizes[j].minimum_size = line->minimum;
sizes[j].natural_size = line->natural;
sizes[j].data = line;
j++;
}
size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
if (expand > 0)
{
extra = size / expand;
rest = size % expand;
}
else
{
extra = 0;
rest = 0;
}
j = 0;
for (i = min - lines->min; i < max - lines->min; i++)
{
line = &lines->lines[i];
if (line->empty)
continue;
g_assert (line == sizes[j].data);
line->allocation = sizes[j].minimum_size;
if (line->expand)
{
line->allocation += extra;
if (rest > 0)
{
line->allocation += 1;
rest -= 1;
}
}
j++;
}
}
/* Requires that the minimum and natural fields of lines
* have been set, computes the allocation field of lines
* by distributing total_size among lines.
*/
static void
grid_request_allocate (GridRequest *request,
GtkOrientation orientation,
int total_size)
{
GtkGridLayout *self = request->layout;
GridLineData *linedata;
GridLines *lines;
GridLine *line;
int nonempty1, nonempty2;
int expand1, expand2;
int i;
GtkBaselinePosition baseline_pos;
int baseline;
int extra, extra2;
int rest;
int size1, size2;
int split, split_pos;
int spacing;
linedata = &self->linedata[orientation];
lines = &request->lines[orientation];
spacing = get_spacing (request->layout, request->widget, orientation);
baseline = gtk_widget_get_allocated_baseline (request->widget);
if (orientation == GTK_ORIENTATION_VERTICAL && baseline != -1 &&
self->baseline_row >= lines->min && self->baseline_row < lines->max &&
lines->lines[self->baseline_row - lines->min].minimum_above != -1)
{
split = self->baseline_row;
split_pos = baseline - lines->lines[self->baseline_row - lines->min].minimum_above;
grid_request_compute_expand (request, orientation, lines->min, split, &nonempty1, &expand1);
grid_request_compute_expand (request, orientation, split, lines->max, &nonempty2, &expand2);
if (nonempty2 > 0)
{
size1 = split_pos - (nonempty1) * spacing;
size2 = (total_size - split_pos) - (nonempty2 - 1) * spacing;
}
else
{
size1 = total_size - (nonempty1 - 1) * spacing;
size2 = 0;
}
}
else
{
grid_request_compute_expand (request, orientation, lines->min, lines->max, &nonempty1, &expand1);
nonempty2 = expand2 = 0;
split = lines->max;
size1 = total_size - (nonempty1 - 1) * spacing;
size2 = 0;
}
if (nonempty1 == 0 && nonempty2 == 0)
return;
if (linedata->homogeneous)
{
if (nonempty1 > 0)
{
extra = size1 / nonempty1;
rest = size1 % nonempty1;
}
else
{
extra = 0;
rest = 0;
}
if (nonempty2 > 0)
{
extra2 = size2 / nonempty2;
if (extra2 < extra || nonempty1 == 0)
{
extra = extra2;
rest = size2 % nonempty2;
}
}
for (i = 0; i < lines->max - lines->min; i++)
{
line = &lines->lines[i];
if (line->empty)
continue;
line->allocation = extra;
if (rest > 0)
{
line->allocation += 1;
rest -= 1;
}
}
}
else
{
grid_distribute_non_homogeneous (lines,
nonempty1,
expand1,
size1,
lines->min,
split);
grid_distribute_non_homogeneous (lines,
nonempty2,
expand2,
size2,
split,
lines->max);
}
for (i = 0; i < lines->max - lines->min; i++)
{
line = &lines->lines[i];
if (line->empty)
continue;
if (line->minimum_above != -1)
{
/* Note: This is overridden in grid_request_position for the allocated baseline */
baseline_pos = gtk_grid_layout_get_row_baseline_position (request->layout, i + lines->min);
switch (baseline_pos)
{
case GTK_BASELINE_POSITION_TOP:
line->allocated_baseline = line->minimum_above;
break;
case GTK_BASELINE_POSITION_CENTER:
line->allocated_baseline = line->minimum_above +
(line->allocation - (line->minimum_above + line->minimum_below)) / 2;
break;
case GTK_BASELINE_POSITION_BOTTOM:
line->allocated_baseline = line->allocation - line->minimum_below;
break;
default:
break;
}
}
else
line->allocated_baseline = -1;
}
}
/* Computes the position fields from allocation and spacing */
static void
grid_request_position (GridRequest *request,
GtkOrientation orientation)
{
GtkGridLayout *self = request->layout;
GridLines *lines;
GridLine *line;
int position, old_position;
int allocated_baseline;
int spacing;
int i, j;
lines = &request->lines[orientation];
spacing = get_spacing (request->layout, request->widget, orientation);
allocated_baseline = gtk_widget_get_allocated_baseline (request->widget);
position = 0;
for (i = 0; i < lines->max - lines->min; i++)
{
line = &lines->lines[i];
if (orientation == GTK_ORIENTATION_VERTICAL &&
i + lines->min == self->baseline_row &&
allocated_baseline != -1 &&
lines->lines[i].minimum_above != -1)
{
old_position = position;
position = allocated_baseline - line->minimum_above;
/* Back-patch previous rows */
for (j = 0; j < i; j++)
{
if (!lines->lines[j].empty)
lines->lines[j].position += position - old_position;
}
}
if (!line->empty)
{
line->position = position;
position += line->allocation + spacing;
if (orientation == GTK_ORIENTATION_VERTICAL &&
i + lines->min == self->baseline_row &&
allocated_baseline != -1 &&
lines->lines[i].minimum_above != -1)
line->allocated_baseline = allocated_baseline - line->position;
}
}
}
static void
gtk_grid_layout_get_size (GtkGridLayout *self,
GtkWidget *widget,
GtkOrientation orientation,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GridRequest request;
GridLines *lines;
*minimum = 0;
*natural = 0;
if (minimum_baseline)
*minimum_baseline = -1;
if (natural_baseline)
*natural_baseline = -1;
if (_gtk_widget_get_first_child (widget) == NULL)
return;
request.layout = self;
request.widget = widget;
grid_request_count_lines (&request);
lines = &request.lines[orientation];
lines->lines = g_newa (GridLine, lines->max - lines->min);
memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GridLine));
grid_request_run (&request, orientation, FALSE);
grid_request_sum (&request, orientation,
minimum, natural,
minimum_baseline, natural_baseline);
}
static void
gtk_grid_layout_get_size_for_size (GtkGridLayout *self,
GtkWidget *widget,
GtkOrientation orientation,
int size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GridRequest request;
GridLines *lines;
int min_size, nat_size;
*minimum = 0;
*natural = 0;
if (minimum_baseline)
*minimum_baseline = -1;
if (natural_baseline)
*natural_baseline = -1;
if (_gtk_widget_get_first_child (widget) == NULL)
return;
request.layout = self;
request.widget = widget;
grid_request_count_lines (&request);
lines = &request.lines[0];
lines->lines = g_newa (GridLine, lines->max - lines->min);
memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GridLine));
lines = &request.lines[1];
lines->lines = g_newa (GridLine, lines->max - lines->min);
memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GridLine));
grid_request_run (&request, 1 - orientation, FALSE);
grid_request_sum (&request, 1 - orientation, &min_size, &nat_size, NULL, NULL);
grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
grid_request_run (&request, orientation, TRUE);
grid_request_sum (&request, orientation,
minimum, natural,
minimum_baseline, natural_baseline);
}
static void
gtk_grid_layout_measure (GtkLayoutManager *manager,
GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkGridLayout *self = GTK_GRID_LAYOUT (manager);
if ((orientation == GTK_ORIENTATION_HORIZONTAL &&
gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT) ||
(orientation == GTK_ORIENTATION_VERTICAL &&
gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH))
gtk_grid_layout_get_size_for_size (self, widget, orientation, for_size,
minimum, natural,
minimum_baseline, natural_baseline);
else
gtk_grid_layout_get_size (self, widget, orientation,
minimum, natural,
minimum_baseline, natural_baseline);
}
static void
allocate_child (GridRequest *request,
GtkOrientation orientation,
GtkWidget *child,
GtkGridLayoutChild *grid_child,
int *position,
int *size,
int *baseline)
{
GridLines *lines;
GridLine *line;
GridChildAttach *attach;
int i;
lines = &request->lines[orientation];
attach = &grid_child->attach[orientation];
*position = lines->lines[attach->pos - lines->min].position;
if (attach->span == 1 && gtk_widget_get_valign (child) == GTK_ALIGN_BASELINE)
*baseline = lines->lines[attach->pos - lines->min].allocated_baseline;
else
*baseline = -1;
*size = (attach->span - 1) * get_spacing (request->layout, request->widget, orientation);
for (i = 0; i < attach->span; i++)
{
line = &lines->lines[attach->pos - lines->min + i];
*size += line->allocation;
}
}
static void
grid_request_allocate_children (GridRequest *request,
int grid_width,
int grid_height)
{
GtkWidget *child;
GtkAllocation child_allocation;
int x, y, width, height, baseline, ignore;
for (child = _gtk_widget_get_first_child (request->widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkGridLayoutChild *grid_child = get_grid_child (request->layout, child);
if (!gtk_widget_should_layout (child))
continue;
allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, grid_child, &x, &width, &ignore);
allocate_child (request, GTK_ORIENTATION_VERTICAL, child, grid_child, &y, &height, &baseline);
child_allocation.x = x;
child_allocation.y = y;
child_allocation.width = width;
child_allocation.height = height;
if (_gtk_widget_get_direction (request->widget) == GTK_TEXT_DIR_RTL)
child_allocation.x = grid_width - child_allocation.x - child_allocation.width;
gtk_widget_size_allocate (child, &child_allocation, baseline);
}
}
#define GET_SIZE(width, height, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? width : height)
static void
gtk_grid_layout_allocate (GtkLayoutManager *manager,
GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkGridLayout *self = GTK_GRID_LAYOUT (manager);
GridRequest request;
GridLines *lines;
GtkOrientation orientation;
if (_gtk_widget_get_first_child (widget) == NULL)
return;
request.layout = self;
request.widget = widget;
grid_request_count_lines (&request);
lines = &request.lines[0];
lines->lines = g_newa (GridLine, lines->max - lines->min);
memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GridLine));
lines = &request.lines[1];
lines->lines = g_newa (GridLine, lines->max - lines->min);
memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GridLine));
if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
orientation = GTK_ORIENTATION_HORIZONTAL;
else
orientation = GTK_ORIENTATION_VERTICAL;
grid_request_run (&request, OPPOSITE_ORIENTATION (orientation), FALSE);
grid_request_allocate (&request, OPPOSITE_ORIENTATION (orientation),
GET_SIZE (width, height, OPPOSITE_ORIENTATION (orientation)));
grid_request_run (&request, orientation, TRUE);
grid_request_allocate (&request, orientation, GET_SIZE (width, height, orientation));
grid_request_position (&request, 0);
grid_request_position (&request, 1);
grid_request_allocate_children (&request, width, height);
}
static void
gtk_grid_layout_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkGridLayout *self = GTK_GRID_LAYOUT (gobject);
switch (prop_id)
{
case PROP_ROW_SPACING:
gtk_grid_layout_set_row_spacing (self, g_value_get_int (value));
break;
case PROP_COLUMN_SPACING:
gtk_grid_layout_set_column_spacing (self, g_value_get_int (value));
break;
case PROP_ROW_HOMOGENEOUS:
gtk_grid_layout_set_row_homogeneous (self, g_value_get_boolean (value));
break;
case PROP_COLUMN_HOMOGENEOUS:
gtk_grid_layout_set_column_homogeneous (self, g_value_get_boolean (value));
break;
case PROP_BASELINE_ROW:
gtk_grid_layout_set_baseline_row (self, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_grid_layout_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkGridLayout *self = GTK_GRID_LAYOUT (gobject);
switch (prop_id)
{
case PROP_ROW_SPACING:
g_value_set_int (value, COLUMNS (self)->spacing);
break;
case PROP_COLUMN_SPACING:
g_value_set_int (value, ROWS (self)->spacing);
break;
case PROP_ROW_HOMOGENEOUS:
g_value_set_boolean (value, COLUMNS (self)->homogeneous);
break;
case PROP_COLUMN_HOMOGENEOUS:
g_value_set_boolean (value, ROWS (self)->homogeneous);
break;
case PROP_BASELINE_ROW:
g_value_set_int (value, self->baseline_row);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gtk_grid_layout_finalize (GObject *gobject)
{
GtkGridLayout *self = GTK_GRID_LAYOUT (gobject);
g_clear_pointer (&self->row_properties, g_array_unref);
G_OBJECT_CLASS (gtk_grid_layout_parent_class)->finalize (gobject);
}
static void
gtk_grid_layout_class_init (GtkGridLayoutClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkLayoutManagerClass *layout_class = GTK_LAYOUT_MANAGER_CLASS (klass);
layout_class->layout_child_type = GTK_TYPE_GRID_LAYOUT_CHILD;
layout_class->measure = gtk_grid_layout_measure;
layout_class->allocate = gtk_grid_layout_allocate;
gobject_class->set_property = gtk_grid_layout_set_property;
gobject_class->get_property = gtk_grid_layout_get_property;
gobject_class->finalize = gtk_grid_layout_finalize;
/**
* GtkGridLayout:row-spacing: (attributes org.gtk.Property.get=gtk_grid_layout_get_row_spacing org.gtk.Property.set=gtk_grid_layout_set_row_spacing)
*
* The amount of space between to consecutive rows.
*/
layout_props[PROP_ROW_SPACING] =
g_param_spec_int ("row-spacing", NULL, NULL,
0, G_MAXINT16, 0,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayout:column-spacing: (attributes org.gtk.Property.get=gtk_grid_layout_get_column_spacing org.gtk.Property.set=gtk_grid_layout_set_column_spacing)
*
* The amount of space between to consecutive columns.
*/
layout_props[PROP_COLUMN_SPACING] =
g_param_spec_int ("column-spacing", NULL, NULL,
0, G_MAXINT16, 0,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayout:row-homogeneous: (attributes org.gtk.Property.get=gtk_grid_layout_get_row_homogeneous org.gtk.Property.set=gtk_grid_layout_set_row_homogeneous)
*
* Whether all the rows in the grid have the same height.
*/
layout_props[PROP_ROW_HOMOGENEOUS] =
g_param_spec_boolean ("row-homogeneous", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayout:column-homogeneous: (attributes org.gtk.Property.get=gtk_grid_layout_get_column_homogeneous org.gtk.Property.set=gtk_grid_layout_set_column_homogeneous)
*
* Whether all the columns in the grid have the same width.
*/
layout_props[PROP_COLUMN_HOMOGENEOUS] =
g_param_spec_boolean ("column-homogeneous", NULL, NULL,
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkGridLayout:baseline-row: (attributes org.gtk.Property.get=gtk_grid_layout_get_baseline_row org.gtk.Property.set=gtk_grid_layout_set_baseline_row)
*
* The row to align to the baseline, when `GtkWidget:valign` is set
* to %GTK_ALIGN_BASELINE.
*/
layout_props[PROP_BASELINE_ROW] =
g_param_spec_int ("baseline-row", NULL, NULL,
0, G_MAXINT, 0,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPERTIES, layout_props);
}
static void
gtk_grid_layout_init (GtkGridLayout *self)
{
}
/**
* gtk_grid_layout_new:
*
* Creates a new `GtkGridLayout`.
*
* Returns: the newly created `GtkGridLayout`
*/
GtkLayoutManager *
gtk_grid_layout_new (void)
{
return g_object_new (GTK_TYPE_GRID_LAYOUT, NULL);
}
/**
* gtk_grid_layout_set_row_homogeneous: (attributes org.gtk.Method.set_property=row-homogeneous)
* @grid: a `GtkGridLayout`
* @homogeneous: %TRUE to make rows homogeneous
*
* Sets whether all rows of @grid should have the same height.
*/
void
gtk_grid_layout_set_row_homogeneous (GtkGridLayout *grid,
gboolean homogeneous)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT (grid));
/* Yes, homogeneous rows means all the columns have the same size */
if (COLUMNS (grid)->homogeneous == !!homogeneous)
return;
COLUMNS (grid)->homogeneous = !!homogeneous;
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (grid));
g_object_notify_by_pspec (G_OBJECT (grid), layout_props[PROP_ROW_HOMOGENEOUS]);
}
/**
* gtk_grid_layout_get_row_homogeneous: (attributes org.gtk.Method.get_property=row-homogeneous)
* @grid: a `GtkGridLayout`
*
* Checks whether all rows of @grid should have the same height.
*
* Returns: %TRUE if the rows are homogeneous, and %FALSE otherwise
*/
gboolean
gtk_grid_layout_get_row_homogeneous (GtkGridLayout *grid)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT (grid), FALSE);
return COLUMNS (grid)->homogeneous;
}
/**
* gtk_grid_layout_set_row_spacing: (attributes org.gtk.Method.set_property=row-spacing)
* @grid: a `GtkGridLayout`
* @spacing: the amount of space between rows, in pixels
*
* Sets the amount of space to insert between consecutive rows.
*/
void
gtk_grid_layout_set_row_spacing (GtkGridLayout *grid,
guint spacing)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT (grid));
g_return_if_fail (spacing <= G_MAXINT16);
if (COLUMNS (grid)->spacing == spacing)
return;
COLUMNS (grid)->spacing = spacing;
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (grid));
g_object_notify_by_pspec (G_OBJECT (grid), layout_props[PROP_ROW_SPACING]);
}
/**
* gtk_grid_layout_get_row_spacing: (attributes org.gtk.Method.get_property=row-spacing)
* @grid: a `GtkGridLayout`
*
* Retrieves the spacing set with gtk_grid_layout_set_row_spacing().
*
* Returns: the spacing between consecutive rows
*/
guint
gtk_grid_layout_get_row_spacing (GtkGridLayout *grid)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT (grid), 0);
return COLUMNS (grid)->spacing;
}
/**
* gtk_grid_layout_set_column_homogeneous: (attributes org.gtk.Method.set_property=column-homogeneous)
* @grid: a `GtkGridLayout`
* @homogeneous: %TRUE to make columns homogeneous
*
* Sets whether all columns of @grid should have the same width.
*/
void
gtk_grid_layout_set_column_homogeneous (GtkGridLayout *grid,
gboolean homogeneous)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT (grid));
/* Yes, homogeneous columns means all the rows have the same size */
if (ROWS (grid)->homogeneous == !!homogeneous)
return;
ROWS (grid)->homogeneous = !!homogeneous;
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (grid));
g_object_notify_by_pspec (G_OBJECT (grid), layout_props[PROP_COLUMN_HOMOGENEOUS]);
}
/**
* gtk_grid_layout_get_column_homogeneous: (attributes org.gtk.Method.get_property=column-homogeneous)
* @grid: a `GtkGridLayout`
*
* Checks whether all columns of @grid should have the same width.
*
* Returns: %TRUE if the columns are homogeneous, and %FALSE otherwise
*/
gboolean
gtk_grid_layout_get_column_homogeneous (GtkGridLayout *grid)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT (grid), FALSE);
return ROWS (grid)->homogeneous;
}
/**
* gtk_grid_layout_set_column_spacing: (attributes org.gtk.Method.set_property=column-spacing)
* @grid: a `GtkGridLayout`
* @spacing: the amount of space between columns, in pixels
*
* Sets the amount of space to insert between consecutive columns.
*/
void
gtk_grid_layout_set_column_spacing (GtkGridLayout *grid,
guint spacing)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT (grid));
g_return_if_fail (spacing <= G_MAXINT16);
if (ROWS (grid)->spacing == spacing)
return;
ROWS (grid)->spacing = spacing;
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (grid));
g_object_notify_by_pspec (G_OBJECT (grid), layout_props[PROP_COLUMN_SPACING]);
}
/**
* gtk_grid_layout_get_column_spacing: (attributes org.gtk.Method.get_property=column-spacing)
* @grid: a `GtkGridLayout`
*
* Retrieves the spacing set with gtk_grid_layout_set_column_spacing().
*
* Returns: the spacing between consecutive columns
*/
guint
gtk_grid_layout_get_column_spacing (GtkGridLayout *grid)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT (grid), 0);
return ROWS (grid)->spacing;
}
static GridRowProperties *
find_row_properties (GtkGridLayout *self,
int row)
{
int i;
if (self->row_properties == NULL)
return NULL;
for (i = 0; i < self->row_properties->len; i++)
{
GridRowProperties *prop = &g_array_index (self->row_properties, GridRowProperties, i);
if (prop->row == row)
return prop;
}
return NULL;
}
static GridRowProperties *
get_row_properties_or_create (GtkGridLayout *self,
int row)
{
GridRowProperties *props;
props = find_row_properties (self, row);
if (props != NULL)
return props;
/* This is the only place where we create the row properties array;
* find_row_properties() is used by getters, so we should not create
* the array there.
*/
if (self->row_properties == NULL)
self->row_properties = g_array_new (FALSE, FALSE, sizeof (GridRowProperties));
g_array_append_vals (self->row_properties, &grid_row_properties_default, 1);
props = &g_array_index (self->row_properties, GridRowProperties, self->row_properties->len - 1);
props->row = row;
return props;
}
static const GridRowProperties *
get_row_properties_or_default (GtkGridLayout *self,
int row)
{
GridRowProperties *props;
props = find_row_properties (self, row);
if (props != NULL)
return props;
return &grid_row_properties_default;
}
/**
* gtk_grid_layout_set_row_baseline_position:
* @grid: a `GtkGridLayout`
* @row: a row index
* @pos: a `GtkBaselinePosition`
*
* Sets how the baseline should be positioned on @row of the
* grid, in case that row is assigned more space than is requested.
*/
void
gtk_grid_layout_set_row_baseline_position (GtkGridLayout *grid,
int row,
GtkBaselinePosition pos)
{
GridRowProperties *props;
g_return_if_fail (GTK_IS_GRID_LAYOUT (grid));
props = get_row_properties_or_create (grid, row);
if (props->baseline_position == pos)
return;
props->baseline_position = pos;
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (grid));
}
/**
* gtk_grid_layout_get_row_baseline_position:
* @grid: a `GtkGridLayout`
* @row: a row index
*
* Returns the baseline position of @row.
*
* If no value has been set with
* [method@Gtk.GridLayout.set_row_baseline_position],
* the default value of %GTK_BASELINE_POSITION_CENTER
* is returned.
*
* Returns: the baseline position of @row
*/
GtkBaselinePosition
gtk_grid_layout_get_row_baseline_position (GtkGridLayout *grid,
int row)
{
const GridRowProperties *props;
g_return_val_if_fail (GTK_IS_GRID_LAYOUT (grid), GTK_BASELINE_POSITION_CENTER);
props = get_row_properties_or_default (grid, row);
return props->baseline_position;
}
/**
* gtk_grid_layout_set_baseline_row: (attributes org.gtk.Method.set_property=baseline-row)
* @grid: a `GtkGridLayout`
* @row: the row index
*
* Sets which row defines the global baseline for the entire grid.
*
* Each row in the grid can have its own local baseline, but only
* one of those is global, meaning it will be the baseline in the
* parent of the @grid.
*/
void
gtk_grid_layout_set_baseline_row (GtkGridLayout *grid,
int row)
{
g_return_if_fail (GTK_IS_GRID_LAYOUT (grid));
if (grid->baseline_row == row)
return;
grid->baseline_row = row;
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (grid));
g_object_notify_by_pspec (G_OBJECT (grid), layout_props[PROP_BASELINE_ROW]);
}
/**
* gtk_grid_layout_get_baseline_row: (attributes org.gtk.Method.get_property=baseline-row)
* @grid: a `GtkGridLayout`
*
* Retrieves the row set with gtk_grid_layout_set_baseline_row().
*
* Returns: the global baseline row
*/
int
gtk_grid_layout_get_baseline_row (GtkGridLayout *grid)
{
g_return_val_if_fail (GTK_IS_GRID_LAYOUT (grid), GTK_BASELINE_POSITION_CENTER);
return grid->baseline_row;
}
/* }}} */