mirror of
https://github.com/brl/mutter.git
synced 2024-12-27 05:12:15 +00:00
e3259435f2
We're starting from scratch.
1634 lines
46 KiB
C
1634 lines
46 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright (C) 2009 Intel Corporation.
|
|
*
|
|
* 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 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/>.
|
|
*
|
|
* Author:
|
|
* Jose Dapena Paz <jdapena@igalia.com>
|
|
*
|
|
* Based on the MX MxTable actor by:
|
|
* Thomas Wood <thomas.wood@intel.com>
|
|
* and ClutterBoxLayout by:
|
|
* Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-table-layout
|
|
* @title: ClutterTableLayout
|
|
* @short_description: A layout manager arranging children in rows
|
|
* and columns
|
|
*
|
|
* The #ClutterTableLayout is a #ClutterLayoutManager implementing the
|
|
* following layout policy:
|
|
*
|
|
* <itemizedlist>
|
|
* <listitem><para>children are arranged in a table</para></listitem>
|
|
* <listitem><para>each child specifies the specific row and column
|
|
* cell to appear;</para></listitem>
|
|
* <listitem><para>a child can also set a span, and this way, take
|
|
* more than one cell both horizontally and vertically;</para></listitem>
|
|
* <listitem><para>each child will be allocated to its natural
|
|
* size or, if set to expand, the available size;</para></listitem>
|
|
* <listitem><para>if a child is set to fill on either (or both)
|
|
* axis, its allocation will match all the available size; the
|
|
* fill layout property only makes sense if the expand property is
|
|
* also set;</para></listitem>
|
|
* <listitem><para>if a child is set to expand but not to fill then
|
|
* it is possible to control the alignment using the horizontal and
|
|
* vertical alignment layout properties.</para></listitem>
|
|
* </itemizedlist>
|
|
*
|
|
* It is possible to control the spacing between children of a
|
|
* #ClutterTableLayout by using clutter_table_layout_set_row_spacing()
|
|
* and clutter_table_layout_set_column_spacing().
|
|
*
|
|
* In order to set the layout properties when packing an actor inside a
|
|
* #ClutterTableLayout you should use the clutter_table_layout_pack()
|
|
* function.
|
|
*
|
|
* A #ClutterTableLayout can use animations to transition between different
|
|
* values of the layout management properties; the easing mode and duration
|
|
* used for the animations are controlled by the
|
|
* #ClutterTableLayout:easing-mode and #ClutterTableLayout:easing-duration
|
|
* properties and their accessor functions.
|
|
*
|
|
* <figure id="table-layout-image">
|
|
* <title>Table layout</title>
|
|
* <para>The image shows a #ClutterTableLayout.</para>
|
|
* <graphic fileref="table-layout.png" format="PNG"/>
|
|
* </figure>
|
|
*
|
|
* #ClutterTableLayout is available since Clutter 1.4
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#include "clutter-table-layout.h"
|
|
|
|
#include "clutter-container.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-layout-meta.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-types.h"
|
|
|
|
#define CLUTTER_TYPE_TABLE_CHILD (clutter_table_child_get_type ())
|
|
#define CLUTTER_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TABLE_CHILD, ClutterTableChild))
|
|
#define CLUTTER_IS_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TABLE_CHILD))
|
|
|
|
#define CLUTTER_TABLE_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TABLE_LAYOUT, ClutterTableLayoutPrivate))
|
|
|
|
typedef struct _ClutterTableChild ClutterTableChild;
|
|
typedef struct _ClutterLayoutMetaClass ClutterTableChildClass;
|
|
|
|
typedef struct _DimensionData {
|
|
gfloat min_size;
|
|
gfloat pref_size;
|
|
gfloat final_size;
|
|
|
|
guint expand : 1;
|
|
guint visible : 1;
|
|
} DimensionData;
|
|
|
|
struct _ClutterTableLayoutPrivate
|
|
{
|
|
ClutterContainer *container;
|
|
|
|
guint col_spacing;
|
|
guint row_spacing;
|
|
|
|
gint n_rows;
|
|
gint n_cols;
|
|
gint active_row;
|
|
gint active_col;
|
|
gint visible_rows;
|
|
gint visible_cols;
|
|
|
|
GArray *columns;
|
|
GArray *rows;
|
|
};
|
|
|
|
struct _ClutterTableChild
|
|
{
|
|
ClutterLayoutMeta parent_instance;
|
|
|
|
gint col;
|
|
gint row;
|
|
|
|
gint col_span;
|
|
gint row_span;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_CHILD_0,
|
|
|
|
PROP_CHILD_ROW,
|
|
PROP_CHILD_COLUMN,
|
|
PROP_CHILD_ROW_SPAN,
|
|
PROP_CHILD_COLUMN_SPAN
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_ROW_SPACING,
|
|
PROP_COLUMN_SPACING
|
|
};
|
|
|
|
GType clutter_table_child_get_type (void);
|
|
|
|
G_DEFINE_TYPE (ClutterTableChild,
|
|
clutter_table_child,
|
|
CLUTTER_TYPE_LAYOUT_META)
|
|
|
|
G_DEFINE_TYPE (ClutterTableLayout,
|
|
clutter_table_layout,
|
|
CLUTTER_TYPE_LAYOUT_MANAGER)
|
|
|
|
/*
|
|
* ClutterBoxChild
|
|
*/
|
|
|
|
static void
|
|
table_child_set_position (ClutterTableChild *self,
|
|
gint col,
|
|
gint row)
|
|
{
|
|
gboolean row_changed = FALSE, col_changed = FALSE;
|
|
|
|
if (self->col != col)
|
|
{
|
|
self->col = col;
|
|
|
|
col_changed = TRUE;
|
|
}
|
|
|
|
if (self->row != row)
|
|
{
|
|
self->row = row;
|
|
|
|
row_changed = TRUE;
|
|
}
|
|
|
|
if (row_changed || col_changed)
|
|
{
|
|
ClutterLayoutManager *layout;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
clutter_layout_manager_layout_changed (layout);
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
if (row_changed)
|
|
g_object_notify (G_OBJECT (self), "row");
|
|
|
|
if (col_changed)
|
|
g_object_notify (G_OBJECT (self), "column");
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
}
|
|
|
|
static void
|
|
table_child_set_span (ClutterTableChild *self,
|
|
gint col_span,
|
|
gint row_span)
|
|
{
|
|
gboolean row_changed = FALSE, col_changed = FALSE;
|
|
|
|
if (self->col_span != col_span)
|
|
{
|
|
self->col_span = col_span;
|
|
|
|
col_changed = TRUE;
|
|
}
|
|
|
|
if (self->row_span != row_span)
|
|
{
|
|
self->row_span = row_span;
|
|
|
|
row_changed = TRUE;
|
|
}
|
|
|
|
if (row_changed || col_changed)
|
|
{
|
|
ClutterLayoutManager *layout;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
clutter_layout_manager_layout_changed (layout);
|
|
|
|
if (row_changed)
|
|
g_object_notify (G_OBJECT (self), "row-span");
|
|
|
|
if (col_changed)
|
|
g_object_notify (G_OBJECT (self), "column-span");
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_table_child_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CHILD_COLUMN:
|
|
table_child_set_position (self,
|
|
g_value_get_int (value),
|
|
self->row);
|
|
break;
|
|
|
|
case PROP_CHILD_ROW:
|
|
table_child_set_position (self,
|
|
self->col,
|
|
g_value_get_int (value));
|
|
break;
|
|
|
|
case PROP_CHILD_COLUMN_SPAN:
|
|
table_child_set_span (self,
|
|
g_value_get_int (value),
|
|
self->row_span);
|
|
break;
|
|
|
|
case PROP_CHILD_ROW_SPAN:
|
|
table_child_set_span (self,
|
|
self->col_span,
|
|
g_value_get_int (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_table_child_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterTableChild *self = CLUTTER_TABLE_CHILD (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CHILD_ROW:
|
|
g_value_set_int (value, self->row);
|
|
break;
|
|
|
|
case PROP_CHILD_COLUMN:
|
|
g_value_set_int (value, self->col);
|
|
break;
|
|
|
|
case PROP_CHILD_ROW_SPAN:
|
|
g_value_set_int (value, self->row_span);
|
|
break;
|
|
|
|
case PROP_CHILD_COLUMN_SPAN:
|
|
g_value_set_int (value, self->col_span);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_table_child_class_init (ClutterTableChildClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
gobject_class->set_property = clutter_table_child_set_property;
|
|
gobject_class->get_property = clutter_table_child_get_property;
|
|
|
|
pspec = g_param_spec_int ("column",
|
|
P_("Column Number"),
|
|
P_("The column the widget resides in"),
|
|
0, G_MAXINT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_COLUMN, pspec);
|
|
|
|
pspec = g_param_spec_int ("row",
|
|
P_("Row Number"),
|
|
P_("The row the widget resides in"),
|
|
0, G_MAXINT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_ROW, pspec);
|
|
|
|
pspec = g_param_spec_int ("column-span",
|
|
P_("Column Span"),
|
|
P_("The number of columns the widget should span"),
|
|
1, G_MAXINT,
|
|
1,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_COLUMN_SPAN, pspec);
|
|
|
|
pspec = g_param_spec_int ("row-span",
|
|
P_("Row Span"),
|
|
P_("The number of rows the widget should span"),
|
|
1, G_MAXINT,
|
|
1,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_ROW_SPAN, pspec);
|
|
}
|
|
|
|
static void
|
|
clutter_table_child_init (ClutterTableChild *self)
|
|
{
|
|
self->col_span = 1;
|
|
self->row_span = 1;
|
|
}
|
|
|
|
static GType
|
|
clutter_table_layout_get_child_meta_type (ClutterLayoutManager *manager)
|
|
{
|
|
return CLUTTER_TYPE_TABLE_CHILD;
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_set_container (ClutterLayoutManager *layout,
|
|
ClutterContainer *container)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
|
|
|
|
priv->container = container;
|
|
}
|
|
|
|
|
|
static void
|
|
update_row_col (ClutterTableLayout *layout,
|
|
ClutterContainer *container)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = layout->priv;
|
|
ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout);
|
|
ClutterActor *actor, *child;
|
|
gint n_cols, n_rows;
|
|
|
|
n_cols = n_rows = 0;
|
|
|
|
if (container == NULL)
|
|
goto out;
|
|
|
|
actor = CLUTTER_ACTOR (container);
|
|
for (child = clutter_actor_get_first_child (actor);
|
|
child != NULL;
|
|
child = clutter_actor_get_next_sibling (child))
|
|
{
|
|
ClutterTableChild *meta;
|
|
|
|
meta =
|
|
CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
|
|
container,
|
|
child));
|
|
|
|
n_cols = MAX (n_cols, meta->col + meta->col_span);
|
|
n_rows = MAX (n_rows, meta->row + meta->row_span);
|
|
}
|
|
|
|
out:
|
|
priv->n_cols = n_cols;
|
|
priv->n_rows = n_rows;
|
|
|
|
}
|
|
|
|
static void
|
|
calculate_col_widths (ClutterTableLayout *self,
|
|
ClutterContainer *container,
|
|
gint for_width)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = self->priv;
|
|
ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
|
|
ClutterActor *actor, *child;
|
|
gint i;
|
|
DimensionData *columns;
|
|
ClutterOrientation orientation = CLUTTER_ORIENTATION_HORIZONTAL;
|
|
|
|
update_row_col (self, container);
|
|
g_array_set_size (priv->columns, 0);
|
|
g_array_set_size (priv->columns, priv->n_cols);
|
|
columns = (DimensionData *) (void *) priv->columns->data;
|
|
|
|
/* reset the visibility of all columns */
|
|
priv->visible_cols = 0;
|
|
for (i = 0; i < priv->n_cols; i++)
|
|
{
|
|
columns[i].expand = FALSE;
|
|
columns[i].visible = FALSE;
|
|
}
|
|
|
|
actor = CLUTTER_ACTOR (container);
|
|
|
|
/* STAGE ONE: calculate column widths for non-spanned children */
|
|
for (child = clutter_actor_get_first_child (actor);
|
|
child != NULL;
|
|
child = clutter_actor_get_next_sibling (child))
|
|
{
|
|
ClutterTableChild *meta;
|
|
DimensionData *col;
|
|
gfloat c_min, c_pref;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta =
|
|
CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
|
|
container,
|
|
child));
|
|
|
|
if (meta->col_span > 1)
|
|
continue;
|
|
|
|
col = &columns[meta->col];
|
|
|
|
if (!col->visible)
|
|
{
|
|
col->visible = TRUE;
|
|
priv->visible_cols += 1;
|
|
}
|
|
|
|
clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref);
|
|
|
|
col->min_size = MAX (col->min_size, c_min);
|
|
col->pref_size = MAX (col->pref_size, c_pref);
|
|
|
|
if (!col->expand)
|
|
col->expand = clutter_actor_needs_expand (child, orientation);
|
|
}
|
|
|
|
/* STAGE TWO: take spanning children into account */
|
|
for (child = clutter_actor_get_first_child (actor);
|
|
child != NULL;
|
|
child = clutter_actor_get_next_sibling (child))
|
|
{
|
|
ClutterTableChild *meta;
|
|
gfloat c_min, c_pref;
|
|
gfloat min_width, pref_width;
|
|
gint start_col, end_col;
|
|
gint n_expand;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta =
|
|
CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
|
|
container,
|
|
child));
|
|
|
|
if (meta->col_span < 2)
|
|
continue;
|
|
|
|
start_col = meta->col;
|
|
end_col = meta->col + meta->col_span - 1;
|
|
|
|
clutter_actor_get_preferred_width (child, -1, &c_min, &c_pref);
|
|
|
|
/* check there is enough room for this actor */
|
|
min_width = 0;
|
|
pref_width = 0;
|
|
n_expand = 0;
|
|
for (i = start_col; i <= end_col; i++)
|
|
{
|
|
min_width += columns[i].min_size;
|
|
pref_width += columns[i].pref_size;
|
|
|
|
if (columns[i].expand)
|
|
n_expand++;
|
|
|
|
if (!columns[i].visible)
|
|
{
|
|
columns[i].visible = TRUE;
|
|
priv->visible_cols += 1;
|
|
}
|
|
|
|
if (!columns[i].expand)
|
|
columns[i].expand = clutter_actor_needs_expand (child, orientation);
|
|
}
|
|
|
|
min_width += priv->col_spacing * (meta->col_span - 1);
|
|
pref_width += priv->col_spacing * (meta->col_span - 1);
|
|
|
|
/* see calculate_row_heights() for comments */
|
|
/* (1) */
|
|
if (c_min > min_width)
|
|
{
|
|
|
|
/* (2) */
|
|
/* we can start from preferred width and decrease */
|
|
if (pref_width > c_min)
|
|
{
|
|
for (i = start_col; i <= end_col; i++)
|
|
columns[i].final_size = columns[i].pref_size;
|
|
|
|
while (pref_width > c_min)
|
|
{
|
|
for (i = start_col; i <= end_col; i++)
|
|
{
|
|
if (columns[i].final_size > columns[i].min_size)
|
|
{
|
|
columns[i].final_size--;
|
|
pref_width--;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = start_col; i <= end_col; i++)
|
|
columns[i].min_size = columns[i].final_size;
|
|
}
|
|
else
|
|
{
|
|
/* (3) */
|
|
/* we can expand from preferred size */
|
|
gfloat expand_by;
|
|
|
|
expand_by = c_pref - pref_width;
|
|
|
|
for (i = start_col; i <= end_col; i++)
|
|
{
|
|
if (n_expand)
|
|
{
|
|
if (columns[i].expand)
|
|
columns[i].min_size = columns[i].pref_size
|
|
+ expand_by / n_expand;
|
|
}
|
|
else
|
|
columns[i].min_size = columns[i].pref_size
|
|
+ expand_by / meta->col_span;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* calculate final widths */
|
|
if (for_width >= 0)
|
|
{
|
|
gfloat min_width, pref_width;
|
|
gint n_expand;
|
|
|
|
min_width = 0;
|
|
pref_width = 0;
|
|
n_expand = 0;
|
|
|
|
for (i = 0; i < self->priv->n_cols; i++)
|
|
{
|
|
pref_width += columns[i].pref_size;
|
|
min_width += columns[i].min_size;
|
|
if (columns[i].expand)
|
|
n_expand++;
|
|
}
|
|
|
|
pref_width += priv->col_spacing * (priv->n_cols - 1);
|
|
min_width += priv->col_spacing * (priv->n_cols - 1);
|
|
|
|
if (for_width <= min_width)
|
|
{
|
|
/* erk, we can't shrink this! */
|
|
for (i = 0; i < priv->n_cols; i++)
|
|
columns[i].final_size = columns[i].min_size;
|
|
|
|
return;
|
|
}
|
|
|
|
if (for_width == pref_width)
|
|
{
|
|
/* perfect! */
|
|
for (i = 0; i < self->priv->n_cols; i++)
|
|
columns[i].final_size = columns[i].pref_size;
|
|
|
|
return;
|
|
}
|
|
|
|
/* for_width is between min_width and pref_width */
|
|
if (for_width < pref_width && for_width > min_width)
|
|
{
|
|
gfloat width;
|
|
|
|
/* shrink columns until they reach min_width */
|
|
|
|
/* start with all columns at preferred size */
|
|
for (i = 0; i < self->priv->n_cols; i++)
|
|
columns[i].final_size = columns[i].pref_size;
|
|
|
|
width = pref_width;
|
|
|
|
while (width > for_width)
|
|
{
|
|
for (i = 0; i < self->priv->n_cols; i++)
|
|
{
|
|
if (columns[i].final_size > columns[i].min_size)
|
|
{
|
|
columns[i].final_size--;
|
|
width--;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* expand columns */
|
|
if (for_width > pref_width)
|
|
{
|
|
gfloat extra_width = for_width - pref_width;
|
|
gint remaining;
|
|
|
|
if (n_expand)
|
|
remaining = (gint) extra_width % n_expand;
|
|
else
|
|
remaining = (gint) extra_width % priv->n_cols;
|
|
|
|
for (i = 0; i < self->priv->n_cols; i++)
|
|
{
|
|
if (columns[i].expand)
|
|
{
|
|
columns[i].final_size = columns[i].pref_size
|
|
+ (extra_width / n_expand);
|
|
}
|
|
else
|
|
columns[i].final_size = columns[i].pref_size;
|
|
}
|
|
|
|
/* distribute the remainder among children */
|
|
i = 0;
|
|
while (remaining)
|
|
{
|
|
columns[i].final_size++;
|
|
i++;
|
|
remaining--;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
calculate_row_heights (ClutterTableLayout *self,
|
|
ClutterContainer *container,
|
|
gint for_height)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = self->priv;
|
|
ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
|
|
ClutterActor *actor, *child;
|
|
gint i;
|
|
DimensionData *rows, *columns;
|
|
ClutterOrientation orientation = CLUTTER_ORIENTATION_VERTICAL;
|
|
|
|
update_row_col (self, container);
|
|
g_array_set_size (priv->rows, 0);
|
|
g_array_set_size (priv->rows, self->priv->n_rows);
|
|
|
|
rows = (DimensionData *) (void *) priv->rows->data;
|
|
columns = (DimensionData *) (void *) priv->columns->data;
|
|
|
|
/* reset the visibility of all rows */
|
|
priv->visible_rows = 0;
|
|
for (i = 0; i < priv->n_rows; i++)
|
|
{
|
|
rows[i].expand = FALSE;
|
|
rows[i].visible = FALSE;
|
|
}
|
|
|
|
actor = CLUTTER_ACTOR (container);
|
|
|
|
/* STAGE ONE: calculate row heights for non-spanned children */
|
|
for (child = clutter_actor_get_first_child (actor);
|
|
child != NULL;
|
|
child = clutter_actor_get_next_sibling (child))
|
|
{
|
|
ClutterTableChild *meta;
|
|
DimensionData *row;
|
|
gfloat c_min, c_pref;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta =
|
|
CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
|
|
container,
|
|
child));
|
|
|
|
if (meta->row_span > 1)
|
|
continue;
|
|
|
|
row = &rows[meta->row];
|
|
|
|
if (!row->visible)
|
|
{
|
|
row->visible = TRUE;
|
|
priv->visible_rows += 1;
|
|
}
|
|
|
|
clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
|
|
&c_min, &c_pref);
|
|
|
|
row->min_size = MAX (row->min_size, c_min);
|
|
row->pref_size = MAX (row->pref_size, c_pref);
|
|
|
|
if (!row->expand)
|
|
row->expand = clutter_actor_needs_expand (child, orientation);
|
|
}
|
|
|
|
/* STAGE TWO: take spanning children into account */
|
|
for (child = clutter_actor_get_first_child (actor);
|
|
child != NULL;
|
|
child = clutter_actor_get_next_sibling (child))
|
|
{
|
|
ClutterTableChild *meta;
|
|
gfloat c_min, c_pref;
|
|
gfloat min_height, pref_height;
|
|
gint start_row, end_row;
|
|
gint n_expand;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta =
|
|
CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
|
|
container,
|
|
child));
|
|
|
|
if (meta->row_span < 2)
|
|
continue;
|
|
|
|
start_row = meta->row;
|
|
end_row = meta->row + meta->row_span - 1;
|
|
|
|
clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
|
|
&c_min, &c_pref);
|
|
|
|
|
|
/* check there is enough room for this actor */
|
|
min_height = 0;
|
|
pref_height = 0;
|
|
n_expand = 0;
|
|
for (i = start_row; i <= end_row; i++)
|
|
{
|
|
min_height += rows[i].min_size;
|
|
pref_height += rows[i].pref_size;
|
|
|
|
if (rows[i].expand)
|
|
n_expand++;
|
|
|
|
if (!rows[i].visible)
|
|
{
|
|
rows[i].visible = TRUE;
|
|
priv->visible_rows += 1;
|
|
}
|
|
|
|
if (!rows[i].expand)
|
|
rows[i].expand = clutter_actor_needs_expand (child, orientation);
|
|
}
|
|
|
|
min_height += priv->row_spacing * (meta->row_span - 1);
|
|
pref_height += priv->row_spacing * (meta->row_span - 1);
|
|
|
|
/* 1) If the minimum height of the rows spanned is less than the
|
|
* minimum height of the child that is spanning them, then we
|
|
* must increase the minimum height of the rows spanned.
|
|
*
|
|
* 2) If the preferred height of the spanned rows is more than
|
|
* the minimum height of the spanning child, then we can start
|
|
* at this size and decrease each row evenly.
|
|
*
|
|
* 3) If the preferred height of the rows is more than the minimum
|
|
* height of the spanned child, then we can start at the preferred
|
|
* height and expand.
|
|
*/
|
|
|
|
/* (1) */
|
|
if (c_min > min_height)
|
|
{
|
|
|
|
/* (2) */
|
|
/* we can start from preferred height and decrease */
|
|
if (pref_height > c_min)
|
|
{
|
|
for (i = start_row; i <= end_row; i++)
|
|
rows[i].final_size = rows[i].pref_size;
|
|
|
|
while (pref_height > c_min)
|
|
{
|
|
for (i = start_row; i <= end_row; i++)
|
|
{
|
|
if (rows[i].final_size > rows[i].min_size)
|
|
{
|
|
rows[i].final_size--;
|
|
pref_height--;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = start_row; i <= end_row; i++)
|
|
rows[i].min_size = rows[i].final_size;
|
|
}
|
|
else
|
|
{
|
|
/* (3) */
|
|
/* we can expand from preferred size */
|
|
gfloat expand_by = c_pref - pref_height;
|
|
|
|
for (i = start_row; i <= end_row; i++)
|
|
{
|
|
if (n_expand)
|
|
{
|
|
if (rows[i].expand)
|
|
rows[i].min_size = rows[i].pref_size
|
|
+ expand_by / n_expand;
|
|
}
|
|
else
|
|
rows[i].min_size = rows[i].pref_size
|
|
+ expand_by / meta->row_span;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* calculate final heights */
|
|
if (for_height >= 0)
|
|
{
|
|
gfloat min_height, pref_height;
|
|
gint n_expand;
|
|
|
|
min_height = 0;
|
|
pref_height = 0;
|
|
n_expand = 0;
|
|
|
|
for (i = 0; i < self->priv->n_rows; i++)
|
|
{
|
|
pref_height += rows[i].pref_size;
|
|
min_height += rows[i].min_size;
|
|
if (rows[i].expand)
|
|
n_expand++;
|
|
}
|
|
|
|
pref_height += priv->row_spacing * (priv->n_rows - 1);
|
|
min_height += priv->row_spacing * (priv->n_rows - 1);
|
|
|
|
if (for_height <= min_height)
|
|
{
|
|
/* erk, we can't shrink this! */
|
|
for (i = 0; i < self->priv->n_rows; i++)
|
|
rows[i].final_size = rows[i].min_size;
|
|
|
|
return;
|
|
}
|
|
|
|
if (for_height == pref_height)
|
|
{
|
|
/* perfect! */
|
|
for (i = 0; i < self->priv->n_rows; i++)
|
|
rows[i].final_size = rows[i].pref_size;
|
|
|
|
return;
|
|
}
|
|
|
|
/* for_height is between min_height and pref_height */
|
|
if (for_height < pref_height && for_height > min_height)
|
|
{
|
|
gfloat height;
|
|
|
|
/* shrink rows until they reach min_height */
|
|
|
|
/* start with all rows at preferred size */
|
|
for (i = 0; i < self->priv->n_rows; i++)
|
|
rows[i].final_size = rows[i].pref_size;
|
|
|
|
height = pref_height;
|
|
|
|
while (height > for_height)
|
|
{
|
|
for (i = 0; i < priv->n_rows; i++)
|
|
{
|
|
if (rows[i].final_size > rows[i].min_size)
|
|
{
|
|
rows[i].final_size--;
|
|
height--;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* expand rows */
|
|
if (for_height > pref_height)
|
|
{
|
|
gfloat extra_height = for_height - pref_height;
|
|
gint remaining;
|
|
|
|
if (n_expand)
|
|
remaining = (gint) extra_height % n_expand;
|
|
else
|
|
remaining = (gint) extra_height % self->priv->n_rows;
|
|
|
|
for (i = 0; i < self->priv->n_rows; i++)
|
|
{
|
|
if (rows[i].expand)
|
|
{
|
|
rows[i].final_size = rows[i].pref_size
|
|
+ (extra_height / n_expand);
|
|
}
|
|
else
|
|
rows[i].final_size = rows[i].pref_size;
|
|
}
|
|
|
|
/* distribute the remainder among children */
|
|
i = 0;
|
|
while (remaining)
|
|
{
|
|
rows[i].final_size++;
|
|
i++;
|
|
remaining--;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
calculate_table_dimensions (ClutterTableLayout *self,
|
|
ClutterContainer *container,
|
|
gfloat for_width,
|
|
gfloat for_height)
|
|
{
|
|
calculate_col_widths (self, container, for_width);
|
|
calculate_row_heights (self, container, for_height);
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_get_preferred_width (ClutterLayoutManager *layout,
|
|
ClutterContainer *container,
|
|
gfloat for_height,
|
|
gfloat *min_width_p,
|
|
gfloat *natural_width_p)
|
|
{
|
|
ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
|
|
ClutterTableLayoutPrivate *priv = self->priv;
|
|
gfloat total_min_width, total_pref_width;
|
|
DimensionData *columns;
|
|
gint i;
|
|
|
|
update_row_col (self, container);
|
|
if (priv->n_cols < 1)
|
|
{
|
|
*min_width_p = 0;
|
|
*natural_width_p = 0;
|
|
return;
|
|
}
|
|
|
|
calculate_table_dimensions (self, container, -1, for_height);
|
|
columns = (DimensionData *) (void *) priv->columns->data;
|
|
|
|
total_min_width = (priv->visible_cols - 1) * (float) priv->col_spacing;
|
|
total_pref_width = total_min_width;
|
|
|
|
for (i = 0; i < priv->n_cols; i++)
|
|
{
|
|
total_min_width += columns[i].min_size;
|
|
total_pref_width += columns[i].pref_size;
|
|
}
|
|
|
|
if (min_width_p)
|
|
*min_width_p = total_min_width;
|
|
|
|
if (natural_width_p)
|
|
*natural_width_p = total_pref_width;
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_get_preferred_height (ClutterLayoutManager *layout,
|
|
ClutterContainer *container,
|
|
gfloat for_width,
|
|
gfloat *min_height_p,
|
|
gfloat *natural_height_p)
|
|
{
|
|
ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
|
|
ClutterTableLayoutPrivate *priv = self->priv;
|
|
gfloat total_min_height, total_pref_height;
|
|
DimensionData *rows;
|
|
gint i;
|
|
|
|
update_row_col (self, container);
|
|
if (priv->n_rows < 1)
|
|
{
|
|
*min_height_p = 0;
|
|
*natural_height_p = 0;
|
|
return;
|
|
}
|
|
|
|
calculate_table_dimensions (self, container, for_width, -1);
|
|
rows = (DimensionData *) (void *) priv->rows->data;
|
|
|
|
total_min_height = (priv->visible_rows - 1) * (float) priv->row_spacing;
|
|
total_pref_height = total_min_height;
|
|
|
|
for (i = 0; i < self->priv->n_rows; i++)
|
|
{
|
|
total_min_height += rows[i].min_size;
|
|
total_pref_height += rows[i].pref_size;
|
|
}
|
|
|
|
if (min_height_p)
|
|
*min_height_p = total_min_height;
|
|
|
|
if (natural_height_p)
|
|
*natural_height_p = total_pref_height;
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_allocate (ClutterLayoutManager *layout,
|
|
ClutterContainer *container,
|
|
const ClutterActorBox *box,
|
|
ClutterAllocationFlags flags)
|
|
{
|
|
ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (layout);
|
|
ClutterTableLayoutPrivate *priv = self->priv;
|
|
ClutterActor *actor, *child;
|
|
gint row_spacing, col_spacing;
|
|
gint i;
|
|
DimensionData *rows, *columns;
|
|
|
|
update_row_col (self, container);
|
|
if (priv->n_cols < 1 || priv->n_rows < 1)
|
|
return;
|
|
|
|
actor = CLUTTER_ACTOR (container);
|
|
|
|
if (clutter_actor_get_n_children (actor) == 0)
|
|
return;
|
|
|
|
col_spacing = (priv->col_spacing);
|
|
row_spacing = (priv->row_spacing);
|
|
|
|
calculate_table_dimensions (self, container,
|
|
box->x2 - box->x1,
|
|
box->y2 - box->y1);
|
|
|
|
rows = (DimensionData *) (void *) priv->rows->data;
|
|
columns = (DimensionData *) (void *) priv->columns->data;
|
|
|
|
for (child = clutter_actor_get_first_child (actor);
|
|
child != NULL;
|
|
child = clutter_actor_get_next_sibling (child))
|
|
{
|
|
gint row, col, row_span, col_span;
|
|
gint col_width, row_height;
|
|
ClutterTableChild *meta;
|
|
ClutterActorBox childbox;
|
|
gint child_x, child_y;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta =
|
|
CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (layout,
|
|
container,
|
|
child));
|
|
|
|
/* get child properties */
|
|
col = meta->col;
|
|
row = meta->row;
|
|
row_span = meta->row_span;
|
|
col_span = meta->col_span;
|
|
|
|
/* initialise the width and height */
|
|
col_width = columns[col].final_size;
|
|
row_height = rows[row].final_size;
|
|
|
|
/* Add the widths of the spanned columns:
|
|
*
|
|
* First check that we have a non-zero span. Then we loop over each of
|
|
* the columns that we're spanning but we stop short if we go past the
|
|
* number of columns in the table. This is necessary to avoid accessing
|
|
* uninitialised memory. We add the spacing in here too since we only
|
|
* want to add as much spacing as times we successfully span.
|
|
*/
|
|
if (col + col_span > priv->n_cols)
|
|
g_warning (G_STRLOC ": column-span exceeds number of columns");
|
|
if (row + row_span > priv->n_rows)
|
|
g_warning (G_STRLOC ": row-span exceeds number of rows");
|
|
|
|
if (col_span > 1)
|
|
{
|
|
for (i = col + 1; i < col + col_span && i < priv->n_cols; i++)
|
|
{
|
|
col_width += columns[i].final_size;
|
|
col_width += col_spacing;
|
|
}
|
|
}
|
|
|
|
/* add the height of the spanned rows */
|
|
if (row_span > 1)
|
|
{
|
|
for (i = row + 1; i < row + row_span && i < priv->n_rows; i++)
|
|
{
|
|
row_height += rows[i].final_size;
|
|
row_height += row_spacing;
|
|
}
|
|
}
|
|
|
|
/* calculate child x */
|
|
child_x = clutter_actor_box_get_x (box);
|
|
for (i = 0; i < col; i++)
|
|
{
|
|
if (columns[i].visible)
|
|
{
|
|
child_x += columns[i].final_size;
|
|
child_x += col_spacing;
|
|
}
|
|
}
|
|
|
|
/* calculate child y */
|
|
child_y = clutter_actor_box_get_y (box);
|
|
for (i = 0; i < row; i++)
|
|
{
|
|
if (rows[i].visible)
|
|
{
|
|
child_y += rows[i].final_size;
|
|
child_y += row_spacing;
|
|
}
|
|
}
|
|
|
|
/* set up childbox */
|
|
childbox.x1 = (float) child_x;
|
|
childbox.x2 = (float) MAX (0, child_x + col_width);
|
|
|
|
childbox.y1 = (float) child_y;
|
|
childbox.y2 = (float) MAX (0, child_y + row_height);
|
|
|
|
clutter_actor_allocate (child, &childbox, flags);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterTableLayout *self = CLUTTER_TABLE_LAYOUT (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_COLUMN_SPACING:
|
|
clutter_table_layout_set_column_spacing (self, g_value_get_uint (value));
|
|
break;
|
|
|
|
case PROP_ROW_SPACING:
|
|
clutter_table_layout_set_row_spacing (self, g_value_get_uint (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (gobject)->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ROW_SPACING:
|
|
g_value_set_uint (value, priv->row_spacing);
|
|
break;
|
|
|
|
case PROP_COLUMN_SPACING:
|
|
g_value_set_uint (value, priv->col_spacing);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_finalize (GObject *gobject)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (gobject)->priv;
|
|
|
|
g_array_free (priv->columns, TRUE);
|
|
g_array_free (priv->rows, TRUE);
|
|
|
|
G_OBJECT_CLASS (clutter_table_layout_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_class_init (ClutterTableLayoutClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
ClutterLayoutManagerClass *layout_class;
|
|
GParamSpec *pspec;
|
|
|
|
layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
|
|
|
|
gobject_class->set_property = clutter_table_layout_set_property;
|
|
gobject_class->get_property = clutter_table_layout_get_property;
|
|
gobject_class->finalize = clutter_table_layout_finalize;
|
|
|
|
layout_class->get_preferred_width =
|
|
clutter_table_layout_get_preferred_width;
|
|
layout_class->get_preferred_height =
|
|
clutter_table_layout_get_preferred_height;
|
|
layout_class->allocate = clutter_table_layout_allocate;
|
|
layout_class->set_container = clutter_table_layout_set_container;
|
|
layout_class->get_child_meta_type =
|
|
clutter_table_layout_get_child_meta_type;
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterTableLayoutPrivate));
|
|
|
|
/**
|
|
* ClutterTableLayout:column-spacing:
|
|
*
|
|
* The spacing between columns of the #ClutterTableLayout, in pixels
|
|
*
|
|
*
|
|
*/
|
|
pspec = g_param_spec_uint ("column-spacing",
|
|
P_("Column Spacing"),
|
|
P_("Spacing between columns"),
|
|
0, G_MAXUINT, 0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_COLUMN_SPACING, pspec);
|
|
|
|
/**
|
|
* ClutterTableLayout:row-spacing:
|
|
*
|
|
* The spacing between rows of the #ClutterTableLayout, in pixels
|
|
*
|
|
*
|
|
*/
|
|
pspec = g_param_spec_uint ("row-spacing",
|
|
P_("Row Spacing"),
|
|
P_("Spacing between rows"),
|
|
0, G_MAXUINT, 0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_ROW_SPACING, pspec);
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_init (ClutterTableLayout *layout)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
layout->priv = priv = CLUTTER_TABLE_LAYOUT_GET_PRIVATE (layout);
|
|
|
|
priv->row_spacing = 0;
|
|
priv->col_spacing = 0;
|
|
|
|
priv->columns = g_array_new (FALSE, TRUE, sizeof (DimensionData));
|
|
priv->rows = g_array_new (FALSE, TRUE, sizeof (DimensionData));
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_new:
|
|
*
|
|
* Creates a new #ClutterTableLayout layout manager
|
|
*
|
|
* Return value: the newly created #ClutterTableLayout
|
|
*
|
|
*
|
|
*/
|
|
ClutterLayoutManager *
|
|
clutter_table_layout_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_TABLE_LAYOUT, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_column_spacing:
|
|
* @layout: a #ClutterTableLayout
|
|
* @spacing: the spacing between columns of the layout, in pixels
|
|
*
|
|
* Sets the spacing between columns of @layout
|
|
*
|
|
*
|
|
*/
|
|
void
|
|
clutter_table_layout_set_column_spacing (ClutterTableLayout *layout,
|
|
guint spacing)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->col_spacing != spacing)
|
|
{
|
|
ClutterLayoutManager *manager;
|
|
|
|
priv->col_spacing = spacing;
|
|
|
|
manager = CLUTTER_LAYOUT_MANAGER (layout);
|
|
clutter_layout_manager_layout_changed (manager);
|
|
|
|
g_object_notify (G_OBJECT (layout), "column-spacing");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_column_spacing:
|
|
* @layout: a #ClutterTableLayout
|
|
*
|
|
* Retrieves the spacing set using clutter_table_layout_set_column_spacing()
|
|
*
|
|
* Return value: the spacing between columns of the #ClutterTableLayout
|
|
*
|
|
*
|
|
*/
|
|
guint
|
|
clutter_table_layout_get_column_spacing (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 0);
|
|
|
|
return layout->priv->col_spacing;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_row_spacing:
|
|
* @layout: a #ClutterTableLayout
|
|
* @spacing: the spacing between rows of the layout, in pixels
|
|
*
|
|
* Sets the spacing between rows of @layout
|
|
*
|
|
*
|
|
*/
|
|
void
|
|
clutter_table_layout_set_row_spacing (ClutterTableLayout *layout,
|
|
guint spacing)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->row_spacing != spacing)
|
|
{
|
|
ClutterLayoutManager *manager;
|
|
|
|
priv->row_spacing = spacing;
|
|
|
|
manager = CLUTTER_LAYOUT_MANAGER (layout);
|
|
clutter_layout_manager_layout_changed (manager);
|
|
|
|
g_object_notify (G_OBJECT (layout), "row-spacing");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_row_spacing:
|
|
* @layout: a #ClutterTableLayout
|
|
*
|
|
* Retrieves the spacing set using clutter_table_layout_set_row_spacing()
|
|
*
|
|
* Return value: the spacing between rows of the #ClutterTableLayout
|
|
*
|
|
*
|
|
*/
|
|
guint
|
|
clutter_table_layout_get_row_spacing (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 0);
|
|
|
|
return layout->priv->row_spacing;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_pack:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor
|
|
* @column: the column the @actor should be put, or -1 to append
|
|
* @row: the row the @actor should be put, or -1 to append
|
|
*
|
|
* Packs @actor inside the #ClutterContainer associated to @layout
|
|
* at the given row and column.
|
|
*
|
|
*
|
|
*/
|
|
void
|
|
clutter_table_layout_pack (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gint column,
|
|
gint row)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
ClutterLayoutManager *manager;
|
|
ClutterLayoutMeta *meta;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->container == NULL)
|
|
{
|
|
g_warning ("The layout of type '%s' must be associated to "
|
|
"a ClutterContainer before adding children",
|
|
G_OBJECT_TYPE_NAME (layout));
|
|
return;
|
|
}
|
|
|
|
update_row_col (CLUTTER_TABLE_LAYOUT (layout), priv->container);
|
|
|
|
clutter_actor_add_child (CLUTTER_ACTOR (priv->container), actor);
|
|
|
|
manager = CLUTTER_LAYOUT_MANAGER (layout);
|
|
meta = clutter_layout_manager_get_child_meta (manager,
|
|
priv->container,
|
|
actor);
|
|
g_assert (CLUTTER_IS_TABLE_CHILD (meta));
|
|
|
|
if (row < 0)
|
|
row = priv->n_rows;
|
|
|
|
if (column < 0)
|
|
column = priv->n_cols;
|
|
|
|
table_child_set_position (CLUTTER_TABLE_CHILD (meta), column, row);
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_span:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @column_span: Column span for @actor
|
|
* @row_span: Row span for @actor
|
|
*
|
|
* Sets the row and column span for @actor
|
|
* inside @layout
|
|
*
|
|
*
|
|
*/
|
|
void
|
|
clutter_table_layout_set_span (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gint column_span,
|
|
gint row_span)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
ClutterLayoutManager *manager;
|
|
ClutterLayoutMeta *meta;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->container == NULL)
|
|
{
|
|
g_warning ("The layout of type '%s' must be associated to "
|
|
"a ClutterContainer before querying layout "
|
|
"properties",
|
|
G_OBJECT_TYPE_NAME (layout));
|
|
return;
|
|
}
|
|
|
|
manager = CLUTTER_LAYOUT_MANAGER (layout);
|
|
meta = clutter_layout_manager_get_child_meta (manager,
|
|
priv->container,
|
|
actor);
|
|
if (meta == NULL)
|
|
{
|
|
g_warning ("No layout meta found for the child of type '%s' "
|
|
"inside the layout manager of type '%s'",
|
|
G_OBJECT_TYPE_NAME (actor),
|
|
G_OBJECT_TYPE_NAME (manager));
|
|
return;
|
|
}
|
|
|
|
g_assert (CLUTTER_IS_TABLE_CHILD (meta));
|
|
|
|
table_child_set_span (CLUTTER_TABLE_CHILD (meta), column_span, row_span);
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_span:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @column_span: (out): return location for the col span
|
|
* @row_span: (out): return location for the row span
|
|
*
|
|
* Retrieves the row and column span for @actor as set using
|
|
* clutter_table_layout_pack() or clutter_table_layout_set_span()
|
|
*
|
|
*
|
|
*/
|
|
void
|
|
clutter_table_layout_get_span (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gint *column_span,
|
|
gint *row_span)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
ClutterLayoutManager *manager;
|
|
ClutterLayoutMeta *meta;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->container == NULL)
|
|
{
|
|
g_warning ("The layout of type '%s' must be associated to "
|
|
"a ClutterContainer before querying layout "
|
|
"properties",
|
|
G_OBJECT_TYPE_NAME (layout));
|
|
return;
|
|
}
|
|
|
|
manager = CLUTTER_LAYOUT_MANAGER (layout);
|
|
meta = clutter_layout_manager_get_child_meta (manager,
|
|
priv->container,
|
|
actor);
|
|
if (meta == NULL)
|
|
{
|
|
g_warning ("No layout meta found for the child of type '%s' "
|
|
"inside the layout manager of type '%s'",
|
|
G_OBJECT_TYPE_NAME (actor),
|
|
G_OBJECT_TYPE_NAME (manager));
|
|
return;
|
|
}
|
|
|
|
g_assert (CLUTTER_IS_TABLE_CHILD (meta));
|
|
|
|
if (column_span)
|
|
*column_span = CLUTTER_TABLE_CHILD (meta)->col_span;
|
|
|
|
if (row_span)
|
|
*row_span = CLUTTER_TABLE_CHILD (meta)->row_span;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_row_count:
|
|
* @layout: A #ClutterTableLayout
|
|
*
|
|
* Retrieve the current number rows in the @layout
|
|
*
|
|
* Returns: the number of rows
|
|
*
|
|
*
|
|
*/
|
|
gint
|
|
clutter_table_layout_get_row_count (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), -1);
|
|
|
|
update_row_col (layout, layout->priv->container);
|
|
return CLUTTER_TABLE_LAYOUT (layout)->priv->n_rows;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_column_count:
|
|
* @layout: A #ClutterTableLayout
|
|
*
|
|
* Retrieve the current number of columns in @layout
|
|
*
|
|
* Returns: the number of columns
|
|
*
|
|
*
|
|
*/
|
|
gint
|
|
clutter_table_layout_get_column_count (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), -1);
|
|
|
|
update_row_col (layout, layout->priv->container);
|
|
return CLUTTER_TABLE_LAYOUT (layout)->priv->n_cols;
|
|
}
|