2019-04-03 18:03:58 +00:00
|
|
|
/* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:gtkgridlayout
|
|
|
|
* @Short_description: Layout manager for grid-like widgets
|
|
|
|
* @Title: GtkGridLayout
|
|
|
|
* @See_also: #GtkBoxLayout
|
|
|
|
*
|
|
|
|
* GtkGridLayout is a layout manager which arranges child widgets in
|
|
|
|
* rows and columns, with arbitrary positions and horizontal/vertical
|
|
|
|
* spans.
|
|
|
|
*
|
|
|
|
* 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 #GtkGridLayoutChild associated to each child widget.
|
|
|
|
*
|
|
|
|
* The behaviour of GtkGrid 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "gtkgridlayout.h"
|
|
|
|
|
|
|
|
#include "gtkcsspositionvalueprivate.h"
|
|
|
|
#include "gtkdebug.h"
|
|
|
|
#include "gtkintl.h"
|
|
|
|
#include "gtklayoutchild.h"
|
2020-04-23 16:48:29 +00:00
|
|
|
#include "gtkorientable.h"
|
2019-04-03 18:03:58 +00:00
|
|
|
#include "gtkprivate.h"
|
|
|
|
#include "gtksizerequest.h"
|
|
|
|
#include "gtkstylecontextprivate.h"
|
|
|
|
#include "gtkwidgetprivate.h"
|
|
|
|
|
|
|
|
/* {{{ GtkGridLayoutChild */
|
|
|
|
typedef struct {
|
|
|
|
int pos;
|
|
|
|
int span;
|
|
|
|
} GridChildAttach;
|
|
|
|
|
|
|
|
struct _GtkGridLayoutChild
|
|
|
|
{
|
|
|
|
GtkLayoutChild parent_instance;
|
|
|
|
|
|
|
|
GridChildAttach attach[2];
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CHILD_LEFT_ATTACH(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
|
|
|
|
#define CHILD_COL_SPAN(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
|
|
|
|
#define CHILD_TOP_ATTACH(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
|
|
|
|
#define CHILD_ROW_SPAN(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_CHILD_LEFT_ATTACH = 1,
|
|
|
|
PROP_CHILD_TOP_ATTACH,
|
|
|
|
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_LEFT_ATTACH:
|
|
|
|
gtk_grid_layout_child_set_left_attach (self, g_value_get_int (value));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_CHILD_TOP_ATTACH:
|
|
|
|
gtk_grid_layout_child_set_top_attach (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_LEFT_ATTACH:
|
|
|
|
g_value_set_int (value, CHILD_LEFT_ATTACH (self));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_CHILD_TOP_ATTACH:
|
|
|
|
g_value_set_int (value, CHILD_TOP_ATTACH (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;
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayoutChild:left-attach:
|
|
|
|
*
|
|
|
|
* The column number to attach the left side of the child to.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
child_props[PROP_CHILD_LEFT_ATTACH] =
|
|
|
|
g_param_spec_int ("left-attach",
|
|
|
|
P_("Left attachment"),
|
|
|
|
P_("The column number to attach the left side of the child to"),
|
|
|
|
G_MININT, G_MAXINT, 0,
|
|
|
|
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayoutChild:top-attach:
|
|
|
|
*
|
|
|
|
* The row number to attach the top side of the child to.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
child_props[PROP_CHILD_TOP_ATTACH] =
|
|
|
|
g_param_spec_int ("top-attach",
|
|
|
|
P_("Top attachment"),
|
|
|
|
P_("The row number to attach the top side of a child widget to"),
|
|
|
|
G_MININT, G_MAXINT, 0,
|
|
|
|
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayoutChild:column-span:
|
|
|
|
*
|
|
|
|
* The number of columns the child spans to.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
child_props[PROP_CHILD_COLUMN_SPAN] =
|
|
|
|
g_param_spec_int ("column-span",
|
|
|
|
P_("Column span"),
|
|
|
|
P_("The number of columns that a child spans"),
|
|
|
|
1, G_MAXINT, 1,
|
|
|
|
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayoutChild:row-span:
|
|
|
|
*
|
|
|
|
* The number of rows the child spans to.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
child_props[PROP_CHILD_ROW_SPAN] =
|
|
|
|
g_param_spec_int ("row-span",
|
|
|
|
P_("Row span"),
|
|
|
|
P_("The number of rows that a child spans"),
|
|
|
|
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)
|
|
|
|
{
|
2019-06-28 12:40:25 +00:00
|
|
|
CHILD_ROW_SPAN (self) = 1;
|
|
|
|
CHILD_COL_SPAN (self) = 1;
|
2019-04-03 18:03:58 +00:00
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_set_top_attach:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
* @attach: the attach point for @child
|
|
|
|
*
|
|
|
|
* Sets the row number to attach the top side of @child.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
void
|
|
|
|
gtk_grid_layout_child_set_top_attach (GtkGridLayoutChild *child,
|
|
|
|
int attach)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child));
|
|
|
|
|
|
|
|
if (CHILD_TOP_ATTACH (child) == attach)
|
|
|
|
return;
|
|
|
|
|
|
|
|
CHILD_TOP_ATTACH (child) = attach;
|
|
|
|
|
|
|
|
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_TOP_ATTACH]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_get_top_attach:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
*
|
|
|
|
* Retrieves the row number to which @child attaches its top side.
|
|
|
|
*
|
|
|
|
* Returns: the row number
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
int
|
|
|
|
gtk_grid_layout_child_get_top_attach (GtkGridLayoutChild *child)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child), 0);
|
|
|
|
|
|
|
|
return CHILD_TOP_ATTACH (child);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_set_left_attach:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
* @attach: the attach point for @child
|
|
|
|
*
|
|
|
|
* Sets the column number to attach the left side of @child.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
void
|
|
|
|
gtk_grid_layout_child_set_left_attach (GtkGridLayoutChild *child,
|
|
|
|
int attach)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child));
|
|
|
|
|
|
|
|
if (CHILD_LEFT_ATTACH (child) == attach)
|
|
|
|
return;
|
|
|
|
|
|
|
|
CHILD_LEFT_ATTACH (child) = attach;
|
|
|
|
|
|
|
|
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_LEFT_ATTACH]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_get_left_attach:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
*
|
|
|
|
* Retrieves the column number to which @child attaches its left side.
|
|
|
|
*
|
|
|
|
* Returns: the column number
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
int
|
|
|
|
gtk_grid_layout_child_get_left_attach (GtkGridLayoutChild *child)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GTK_IS_GRID_LAYOUT_CHILD (child), 0);
|
|
|
|
|
|
|
|
return CHILD_LEFT_ATTACH (child);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_set_column_span:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
* @span: the span of @child
|
|
|
|
*
|
|
|
|
* Sets the number of columns @child spans to.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_get_column_span:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
*
|
|
|
|
* Retrieves the number of columns that @child spans to.
|
|
|
|
*
|
|
|
|
* Returns: the number of columns
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_set_row_span:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
* @span: the span of @child
|
|
|
|
*
|
|
|
|
* Sets the number of rows @child spans to.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_child_get_row_span:
|
|
|
|
* @child: a #GtkGridLayoutChild
|
|
|
|
*
|
|
|
|
* Retrieves the number of rows that @child spans to.
|
|
|
|
*
|
|
|
|
* Returns: the number of row
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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 GTK_GRID_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_spacing (GtkGridLayout *self,
|
|
|
|
GtkWidget *widget,
|
|
|
|
GtkOrientation orientation)
|
|
|
|
{
|
2020-02-03 10:43:15 +00:00
|
|
|
GtkCssNode *node = gtk_widget_get_css_node (widget);
|
|
|
|
GtkCssStyle *style = gtk_css_node_get_style (node);
|
2019-04-03 18:03:58 +00:00
|
|
|
GtkCssValue *border_spacing;
|
2020-02-03 10:43:15 +00:00
|
|
|
int css_spacing;
|
|
|
|
|
|
|
|
border_spacing = style->size->border_spacing;
|
2019-04-03 18:03:58 +00:00
|
|
|
|
|
|
|
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 gint
|
|
|
|
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;
|
|
|
|
|
2019-06-10 18:01:59 +00:00
|
|
|
if (!gtk_widget_should_layout (child))
|
2019-04-03 18:03:58 +00:00
|
|
|
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;
|
|
|
|
gint minimum, natural;
|
|
|
|
gint 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?
|
2019-04-04 15:31:56 +00:00
|
|
|
* And if so, also in the homogeneous resize.
|
2019-04-03 18:03:58 +00:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2019-06-10 18:01:59 +00:00
|
|
|
if (!gtk_widget_should_layout (child))
|
2019-04-03 18:03:58 +00:00
|
|
|
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);
|
|
|
|
|
2019-06-10 18:01:59 +00:00
|
|
|
if (!gtk_widget_should_layout (child))
|
2019-04-03 18:03:58 +00:00
|
|
|
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);
|
|
|
|
|
2019-06-10 18:01:59 +00:00
|
|
|
if (!gtk_widget_should_layout (child))
|
2019-04-03 18:03:58 +00:00
|
|
|
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;
|
|
|
|
gint 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;
|
|
|
|
gint 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);
|
|
|
|
|
2019-06-10 18:01:59 +00:00
|
|
|
if (!gtk_widget_should_layout (child))
|
2019-04-03 18:03:58 +00:00
|
|
|
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;
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayout:row-spacing:
|
|
|
|
*
|
|
|
|
* The amount of space between to consecutive rows.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
layout_props[PROP_ROW_SPACING] =
|
|
|
|
g_param_spec_int ("row-spacing",
|
|
|
|
P_("Row spacing"),
|
|
|
|
P_("The amount of space between two consecutive rows"),
|
|
|
|
0, G_MAXINT16, 0,
|
|
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayout:column-spacing:
|
|
|
|
*
|
|
|
|
* The amount of space between to consecutive columns.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
layout_props[PROP_COLUMN_SPACING] =
|
|
|
|
g_param_spec_int ("column-spacing",
|
|
|
|
P_("Column spacing"),
|
|
|
|
P_("The amount of space between two consecutive columns"),
|
|
|
|
0, G_MAXINT16, 0,
|
|
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayout:row-homogeneous:
|
|
|
|
*
|
|
|
|
* Whether all the rows in the grid have the same height.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
layout_props[PROP_ROW_HOMOGENEOUS] =
|
|
|
|
g_param_spec_boolean ("row-homogeneous",
|
|
|
|
P_("Row Homogeneous"),
|
|
|
|
P_("If TRUE, the rows are all the same height"),
|
|
|
|
FALSE,
|
|
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayout:column-homogeneous:
|
|
|
|
*
|
|
|
|
* Whether all the columns in the grid have the same width.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
layout_props[PROP_COLUMN_HOMOGENEOUS] =
|
|
|
|
g_param_spec_boolean ("column-homogeneous",
|
|
|
|
P_("Column Homogeneous"),
|
|
|
|
P_("If TRUE, the columns are all the same width"),
|
|
|
|
FALSE,
|
|
|
|
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* GtkGridLayout:baseline-row:
|
|
|
|
*
|
|
|
|
* The row to align to the baseline, when #GtkWidget:valign is set
|
|
|
|
* to %GTK_ALIGN_BASELINE.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
layout_props[PROP_BASELINE_ROW] =
|
|
|
|
g_param_spec_int ("baseline-row",
|
|
|
|
P_("Baseline Row"),
|
|
|
|
P_("The row to align the to the baseline when valign is GTK_ALIGN_BASELINE"),
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-05-02 02:57:49 +00:00
|
|
|
* gtk_grid_layout_new:
|
2019-04-03 18:03:58 +00:00
|
|
|
*
|
|
|
|
* 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:
|
|
|
|
* @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:
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_set_row_spacing:
|
|
|
|
* @grid: a #GtkGridLayout
|
|
|
|
* @spacing: the amount of space between rows, in pixels
|
|
|
|
*
|
|
|
|
* Sets the amount of space to insert between consecutive rows.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_get_row_spacing:
|
|
|
|
* @grid: a #GtkGridLayout
|
|
|
|
*
|
|
|
|
* Retrieves the spacing set with gtk_grid_layout_set_row_spacing().
|
|
|
|
*
|
|
|
|
* Returns: the spacing between consecutive rows
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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:
|
|
|
|
* @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:
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_set_column_spacing:
|
|
|
|
* @grid: a #GtkGridLayout
|
|
|
|
* @spacing: the amount of space between columns, in pixels
|
|
|
|
*
|
|
|
|
* Sets the amount of space to insert between consecutive columns.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_get_column_spacing:
|
|
|
|
* @grid: a #GtkGridLayout
|
|
|
|
*
|
|
|
|
* Retrieves the spacing set with gtk_grid_layout_set_column_spacing().
|
|
|
|
*
|
|
|
|
* Returns: the spacing between consecutive columns
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_get_row_baseline_position:
|
|
|
|
* @grid: a #GtkGridLayout
|
|
|
|
* @row: a row index
|
|
|
|
*
|
|
|
|
* Returns the baseline position of @row as set by
|
|
|
|
* gtk_grid_layout_set_row_baseline_position(), or the default value
|
|
|
|
* of %GTK_BASELINE_POSITION_CENTER.
|
|
|
|
*
|
|
|
|
* Returns: the baseline position of @row
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_set_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.
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2019-04-04 15:31:56 +00:00
|
|
|
/**
|
|
|
|
* gtk_grid_layout_get_baseline_row:
|
|
|
|
* @grid: a #GtkGridLayout
|
|
|
|
*
|
|
|
|
* Retrieves the row set with gtk_grid_layout_set_baseline_row().
|
|
|
|
*
|
|
|
|
* Returns: the global baseline row
|
|
|
|
*/
|
2019-04-03 18:03:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
/* }}} */
|