4724be167f
Whenever the allocation is changed on a child of a ClutterTableLayout and animations are not in effect then it would store a copy of the allocation in the child meta data. However it was not freeing the old copy of the allocation so it would end up with a small leak. Instead of just changing it to free the old value this patch makes it store the allocation inline in the meta data struct because it seems that the size of an actor box is already quite small compared to the size of the meta data struct so it is probably not worth having a separate allocation for it. To detect the case when there has not yet been an allocation a separate boolean is used instead of storing NULL. http://bugzilla.clutter-project.org/show_bug.cgi?id=2358
2636 lines
75 KiB
C
2636 lines
75 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.
|
|
*
|
|
* #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-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 shrink : 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;
|
|
|
|
gulong easing_mode;
|
|
guint easing_duration;
|
|
|
|
guint is_animating : 1;
|
|
guint use_animations : 1;
|
|
};
|
|
|
|
struct _ClutterTableChild
|
|
{
|
|
ClutterLayoutMeta parent_instance;
|
|
|
|
/* the last stable allocation before an animation; it is used as the
|
|
* initial ActorBox when interpolating. If has_last_allocation is
|
|
* FALSE then this value is not yet known
|
|
*/
|
|
ClutterActorBox last_allocation;
|
|
|
|
gint col;
|
|
gint row;
|
|
|
|
gint col_span;
|
|
gint row_span;
|
|
|
|
ClutterTableAlignment x_align;
|
|
ClutterTableAlignment y_align;
|
|
|
|
guint x_expand : 1;
|
|
guint y_expand : 1;
|
|
guint x_fill : 1;
|
|
guint y_fill : 1;
|
|
guint has_last_allocation : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_CHILD_0,
|
|
|
|
PROP_CHILD_ROW,
|
|
PROP_CHILD_COLUMN,
|
|
PROP_CHILD_ROW_SPAN,
|
|
PROP_CHILD_COLUMN_SPAN,
|
|
PROP_CHILD_X_ALIGN,
|
|
PROP_CHILD_Y_ALIGN,
|
|
PROP_CHILD_X_FILL,
|
|
PROP_CHILD_Y_FILL,
|
|
PROP_CHILD_X_EXPAND,
|
|
PROP_CHILD_Y_EXPAND
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_ROW_SPACING,
|
|
PROP_COLUMN_SPACING,
|
|
PROP_USE_ANIMATIONS,
|
|
PROP_EASING_MODE,
|
|
PROP_EASING_DURATION
|
|
};
|
|
|
|
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;
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
if (priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (layout,
|
|
priv->easing_duration,
|
|
priv->easing_mode);
|
|
}
|
|
else
|
|
clutter_layout_manager_layout_changed (layout);
|
|
|
|
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;
|
|
ClutterTableLayout *table;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
table = CLUTTER_TABLE_LAYOUT (layout);
|
|
|
|
if (table->priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (layout,
|
|
table->priv->easing_duration,
|
|
table->priv->easing_mode);
|
|
}
|
|
else
|
|
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
|
|
table_child_set_align (ClutterTableChild *self,
|
|
ClutterTableAlignment x_align,
|
|
ClutterTableAlignment y_align)
|
|
{
|
|
gboolean x_changed = FALSE, y_changed = FALSE;
|
|
|
|
if (self->x_align != x_align)
|
|
{
|
|
self->x_align = x_align;
|
|
|
|
x_changed = TRUE;
|
|
}
|
|
|
|
if (self->y_align != y_align)
|
|
{
|
|
self->y_align = y_align;
|
|
|
|
y_changed = TRUE;
|
|
}
|
|
|
|
if (x_changed || y_changed)
|
|
{
|
|
ClutterLayoutManager *layout;
|
|
ClutterTableLayout *table;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
table = CLUTTER_TABLE_LAYOUT (layout);
|
|
|
|
if (table->priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (layout,
|
|
table->priv->easing_duration,
|
|
table->priv->easing_mode);
|
|
}
|
|
else
|
|
clutter_layout_manager_layout_changed (layout);
|
|
|
|
if (x_changed)
|
|
g_object_notify (G_OBJECT (self), "x-align");
|
|
|
|
if (y_changed)
|
|
g_object_notify (G_OBJECT (self), "y-align");
|
|
}
|
|
}
|
|
|
|
static void
|
|
table_child_set_fill (ClutterTableChild *self,
|
|
gboolean x_fill,
|
|
gboolean y_fill)
|
|
{
|
|
gboolean x_changed = FALSE, y_changed = FALSE;
|
|
|
|
x_fill = !!x_fill;
|
|
y_fill = !!y_fill;
|
|
|
|
if (self->x_fill != x_fill)
|
|
{
|
|
self->x_fill = x_fill;
|
|
|
|
x_changed = TRUE;
|
|
}
|
|
|
|
if (self->y_fill != y_fill)
|
|
{
|
|
self->y_fill = y_fill;
|
|
|
|
y_changed = TRUE;
|
|
}
|
|
|
|
if (x_changed || y_changed)
|
|
{
|
|
ClutterLayoutManager *layout;
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
if (priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (layout,
|
|
priv->easing_duration,
|
|
priv->easing_mode);
|
|
}
|
|
else
|
|
clutter_layout_manager_layout_changed (layout);
|
|
|
|
if (x_changed)
|
|
g_object_notify (G_OBJECT (self), "x-fill");
|
|
|
|
if (y_changed)
|
|
g_object_notify (G_OBJECT (self), "y-fill");
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
}
|
|
|
|
static void
|
|
table_child_set_expand (ClutterTableChild *self,
|
|
gboolean x_expand,
|
|
gboolean y_expand)
|
|
{
|
|
gboolean x_changed = FALSE, y_changed = FALSE;
|
|
|
|
x_expand = !!x_expand;
|
|
y_expand = !!y_expand;
|
|
|
|
if (self->x_expand != x_expand)
|
|
{
|
|
self->x_expand = x_expand;
|
|
|
|
x_changed = TRUE;
|
|
}
|
|
|
|
if (self->y_expand != y_expand)
|
|
{
|
|
self->y_expand = y_expand;
|
|
|
|
y_changed = TRUE;
|
|
}
|
|
|
|
if (x_changed || y_changed)
|
|
{
|
|
ClutterLayoutManager *layout;
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self));
|
|
priv = CLUTTER_TABLE_LAYOUT (layout)->priv;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
if (priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (layout,
|
|
priv->easing_duration,
|
|
priv->easing_mode);
|
|
}
|
|
else
|
|
clutter_layout_manager_layout_changed (layout);
|
|
|
|
if (x_changed)
|
|
g_object_notify (G_OBJECT (self), "x-expand");
|
|
|
|
if (y_changed)
|
|
g_object_notify (G_OBJECT (self), "y-expand");
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
case PROP_CHILD_X_ALIGN:
|
|
table_child_set_align (self,
|
|
g_value_get_enum (value),
|
|
self->y_align);
|
|
break;
|
|
|
|
case PROP_CHILD_Y_ALIGN:
|
|
table_child_set_align (self,
|
|
self->x_align,
|
|
g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_CHILD_X_FILL:
|
|
table_child_set_fill (self,
|
|
g_value_get_boolean (value),
|
|
self->y_fill);
|
|
break;
|
|
|
|
case PROP_CHILD_Y_FILL:
|
|
table_child_set_fill (self,
|
|
self->x_fill,
|
|
g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_CHILD_X_EXPAND:
|
|
table_child_set_expand (self,
|
|
g_value_get_boolean (value),
|
|
self->y_expand);
|
|
break;
|
|
|
|
case PROP_CHILD_Y_EXPAND:
|
|
table_child_set_expand (self,
|
|
self->x_expand,
|
|
g_value_get_boolean (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;
|
|
|
|
case PROP_CHILD_X_ALIGN:
|
|
g_value_set_enum (value, self->x_align);
|
|
break;
|
|
|
|
case PROP_CHILD_Y_ALIGN:
|
|
g_value_set_enum (value, self->y_align);
|
|
break;
|
|
|
|
case PROP_CHILD_X_FILL:
|
|
g_value_set_boolean (value, self->x_fill);
|
|
break;
|
|
|
|
case PROP_CHILD_Y_FILL:
|
|
g_value_set_boolean (value, self->y_fill);
|
|
break;
|
|
|
|
case PROP_CHILD_X_EXPAND:
|
|
g_value_set_boolean (value, self->x_expand);
|
|
break;
|
|
|
|
case PROP_CHILD_Y_EXPAND:
|
|
g_value_set_boolean (value, self->y_expand);
|
|
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);
|
|
|
|
pspec = g_param_spec_boolean ("x-expand",
|
|
P_("Horizontal Expand"),
|
|
P_("Allocate extra space for the child in horizontal axis"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_X_EXPAND, pspec);
|
|
|
|
pspec = g_param_spec_boolean ("y-expand",
|
|
P_("Vertical Expand"),
|
|
P_("Allocate extra space for the child in vertical axis"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_Y_EXPAND, pspec);
|
|
|
|
pspec = g_param_spec_boolean ("x-fill",
|
|
P_("Horizontal Fill"),
|
|
P_("Whether the child should receive priority when the container is allocating spare space on the horizontal axis"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_X_FILL, pspec);
|
|
|
|
pspec = g_param_spec_boolean ("y-fill",
|
|
P_("Vertical Fill"),
|
|
P_("Whether the child should receive priority when the container is allocating spare space on the vertical axis"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_Y_FILL, pspec);
|
|
|
|
/**
|
|
* ClutterTableLayout:x-align:
|
|
*
|
|
* The horizontal alignment of the actor within the cell
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_enum ("x-align",
|
|
P_("Horizontal Alignment"),
|
|
P_("Horizontal alignment of the actor within the cell"),
|
|
CLUTTER_TYPE_TABLE_ALIGNMENT,
|
|
CLUTTER_TABLE_ALIGNMENT_CENTER,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec);
|
|
|
|
/**
|
|
* ClutterTableLayout:y-align:
|
|
*
|
|
* The vertical alignment of the actor within the cell
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_enum ("y-align",
|
|
P_("Vertical Alignment"),
|
|
P_("Vertical alignment of the actor within the cell"),
|
|
CLUTTER_TYPE_TABLE_ALIGNMENT,
|
|
CLUTTER_TABLE_ALIGNMENT_CENTER,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_CHILD_Y_ALIGN, pspec);
|
|
}
|
|
|
|
static void
|
|
clutter_table_child_init (ClutterTableChild *self)
|
|
{
|
|
self->col_span = 1;
|
|
self->row_span = 1;
|
|
|
|
self->x_align = CLUTTER_TABLE_ALIGNMENT_CENTER;
|
|
self->y_align = CLUTTER_TABLE_ALIGNMENT_CENTER;
|
|
|
|
self->x_expand = TRUE;
|
|
self->y_expand = TRUE;
|
|
|
|
self->x_fill = TRUE;
|
|
self->y_fill = TRUE;
|
|
|
|
self->has_last_allocation = FALSE;
|
|
}
|
|
|
|
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);
|
|
GList *children, *l;
|
|
gint n_cols, n_rows;
|
|
|
|
n_cols = n_rows = 0;
|
|
children = container ? clutter_container_get_children (container) : NULL;
|
|
|
|
for (l = children; l; l = g_list_next (l))
|
|
{
|
|
ClutterActor *child = l->data;
|
|
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);
|
|
}
|
|
|
|
g_list_free (children);
|
|
|
|
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);
|
|
gint i;
|
|
DimensionData *columns;
|
|
GList *l, *children;
|
|
|
|
update_row_col (self, container);
|
|
g_array_set_size (priv->columns, 0);
|
|
g_array_set_size (priv->columns, priv->n_cols);
|
|
columns = (DimensionData *) priv->columns->data;
|
|
|
|
/* reset the visibility of all columns */
|
|
priv->visible_cols = 0;
|
|
for (i = 0; i < priv->n_cols; i++)
|
|
columns[i].visible = FALSE;
|
|
|
|
children = clutter_container_get_children (container);
|
|
|
|
/* STAGE ONE: calculate column widths for non-spanned children */
|
|
for (l = children; l; l = g_list_next (l))
|
|
{
|
|
ClutterActor *child = l->data;
|
|
ClutterTableChild *meta;
|
|
DimensionData *col;
|
|
gfloat c_min, c_pref;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta = (ClutterTableChild *)
|
|
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);
|
|
col->expand = MAX (col->expand, meta->x_expand);
|
|
}
|
|
|
|
/* STAGE TWO: take spanning children into account */
|
|
for (l = children; l; l = g_list_next (l))
|
|
{
|
|
ClutterActor *child = l->data;
|
|
ClutterTableChild *meta;
|
|
DimensionData *col;
|
|
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 = (ClutterTableChild *)
|
|
clutter_layout_manager_get_child_meta (manager, container, child);
|
|
|
|
if (meta->col_span < 2)
|
|
continue;
|
|
|
|
col = &columns[meta->col];
|
|
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 (!col->visible)
|
|
{
|
|
col->visible = TRUE;
|
|
priv->visible_cols += 1;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
g_list_free (children);
|
|
|
|
/* 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)
|
|
{
|
|
if (n_expand)
|
|
columns[i].final_size = columns[i].pref_size
|
|
+ (extra_width / n_expand);
|
|
else
|
|
columns[i].final_size = columns[i].pref_size
|
|
+ (extra_width / priv->n_cols);
|
|
}
|
|
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);
|
|
GList *l, *children;
|
|
gint i;
|
|
DimensionData *rows, *columns;
|
|
|
|
update_row_col (self, container);
|
|
g_array_set_size (priv->rows, 0);
|
|
g_array_set_size (priv->rows, self->priv->n_rows);
|
|
|
|
rows = (DimensionData*) priv->rows->data;
|
|
columns = (DimensionData*) priv->columns->data;
|
|
|
|
/* reset the visibility of all rows */
|
|
priv->visible_rows = 0;
|
|
for (i = 0; i < priv->n_rows; i++)
|
|
rows[i].visible = FALSE;
|
|
|
|
children = clutter_container_get_children (container);
|
|
/* STAGE ONE: calculate row heights for non-spanned children */
|
|
for (l = children; l; l = g_list_next (l))
|
|
{
|
|
ClutterActor *child = l->data;
|
|
ClutterTableChild *meta;
|
|
DimensionData *row;
|
|
gfloat c_min, c_pref;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta = (ClutterTableChild *)
|
|
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);
|
|
row->expand = MAX (row->expand, meta->y_expand);
|
|
}
|
|
|
|
|
|
|
|
/* STAGE TWO: take spanning children into account */
|
|
for (l = children; l; l = g_list_next (l))
|
|
{
|
|
ClutterActor *child = l->data;
|
|
ClutterTableChild *meta;
|
|
DimensionData *row;
|
|
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 = (ClutterTableChild *)
|
|
clutter_layout_manager_get_child_meta (manager, container, child);
|
|
|
|
if (meta->row_span < 2)
|
|
continue;
|
|
|
|
row = &rows[meta->row];
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
g_list_free (children);
|
|
|
|
/* 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)
|
|
{
|
|
if (n_expand)
|
|
rows[i].final_size = rows[i].pref_size
|
|
+ (extra_height / n_expand);
|
|
else
|
|
rows[i].final_size = rows[i].pref_size
|
|
+ (extra_height / priv->n_rows);
|
|
}
|
|
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 *) 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 *) 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 gdouble
|
|
get_table_alignment_factor (ClutterTableAlignment alignment)
|
|
{
|
|
switch (alignment)
|
|
{
|
|
case CLUTTER_TABLE_ALIGNMENT_START:
|
|
return 0.0;
|
|
|
|
case CLUTTER_TABLE_ALIGNMENT_CENTER:
|
|
return 0.5;
|
|
|
|
case CLUTTER_TABLE_ALIGNMENT_END:
|
|
return 1.0;
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
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;
|
|
GList *list, *children;
|
|
gint row_spacing, col_spacing;
|
|
gint i, table_width, table_height;
|
|
DimensionData *rows, *columns;
|
|
|
|
update_row_col (self, container);
|
|
if (priv->n_cols < 1 || priv->n_rows < 1)
|
|
return;
|
|
|
|
children = clutter_container_get_children (container);
|
|
if (children == NULL)
|
|
return;
|
|
|
|
col_spacing = (priv->col_spacing);
|
|
row_spacing = (priv->row_spacing);
|
|
|
|
|
|
table_height = (int)(box->y2 - box->y1);
|
|
table_width = (int)(box->x2 - box->x1);
|
|
|
|
calculate_table_dimensions (self, container, box->x2 - box->x1, box->y2 - box->y1);
|
|
|
|
rows = (DimensionData *) priv->rows->data;
|
|
columns = (DimensionData *) priv->columns->data;
|
|
|
|
for (list = children; list; list = g_list_next (list))
|
|
{
|
|
ClutterActor *child = list->data;
|
|
gint row, col, row_span, col_span;
|
|
gint col_width, row_height;
|
|
ClutterTableChild *meta;
|
|
ClutterActorBox childbox;
|
|
gint child_x, child_y;
|
|
gdouble x_align, y_align;
|
|
gboolean x_fill, y_fill;
|
|
|
|
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
|
continue;
|
|
|
|
meta = (ClutterTableChild *)
|
|
clutter_layout_manager_get_child_meta (layout, priv->container, child);
|
|
|
|
/* get child properties */
|
|
col = meta->col;
|
|
row = meta->row;
|
|
row_span = meta->row_span;
|
|
col_span = meta->col_span;
|
|
x_align = get_table_alignment_factor (meta->x_align);
|
|
y_align = get_table_alignment_factor (meta->y_align);
|
|
x_fill = meta->x_fill;
|
|
y_fill = meta->y_fill;
|
|
|
|
/* 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 = 0.0f;
|
|
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 = 0.0f;
|
|
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_align_fill (child, &childbox,
|
|
x_align, y_align,
|
|
x_fill, y_fill,
|
|
flags);
|
|
|
|
/* since we call this after allocate_align_fill(), this is
|
|
* just a cheap copy
|
|
*/
|
|
clutter_actor_get_allocation_box (child, &childbox);
|
|
|
|
if (priv->use_animations && priv->is_animating)
|
|
{
|
|
ClutterActorBox *start = NULL;
|
|
ClutterActorBox end = { 0, };
|
|
gdouble p;
|
|
|
|
p = clutter_layout_manager_get_animation_progress (layout);
|
|
|
|
if (!meta->has_last_allocation)
|
|
{
|
|
/* if there is no allocation available then the child has just
|
|
* been added to the container; we put it in the final state
|
|
* and store its allocation for later
|
|
*/
|
|
meta->last_allocation = childbox;
|
|
meta->has_last_allocation = TRUE;
|
|
|
|
goto do_allocate;
|
|
}
|
|
|
|
start = &meta->last_allocation;
|
|
end = childbox;
|
|
|
|
/* interpolate between the initial and final values */
|
|
clutter_actor_box_interpolate (start, &end, p, &childbox);
|
|
|
|
CLUTTER_NOTE (ANIMATION,
|
|
"Animate { %.1f, %.1f, %.1f, %.1f }\t"
|
|
"%.3f * { %.1f, %.1f, %.1f, %.1f }\t"
|
|
"-> { %.1f, %.1f, %.1f, %.1f }",
|
|
start->x1, start->y1,
|
|
start->x2, start->y2,
|
|
p,
|
|
childbox.x1, childbox.y1,
|
|
childbox.x2, childbox.y2,
|
|
end.x1, end.y1,
|
|
end.x2, end.y2);
|
|
}
|
|
else
|
|
{
|
|
/* store the allocation for later animations */
|
|
meta->last_allocation = childbox;
|
|
meta->has_last_allocation = TRUE;
|
|
}
|
|
|
|
do_allocate:
|
|
clutter_actor_allocate (child, &childbox, flags);
|
|
}
|
|
|
|
g_list_free (children);
|
|
}
|
|
|
|
static ClutterAlpha *
|
|
clutter_table_layout_begin_animation (ClutterLayoutManager *manager,
|
|
guint duration,
|
|
gulong easing)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (manager)->priv;
|
|
ClutterLayoutManagerClass *parent_class;
|
|
|
|
priv->is_animating = TRUE;
|
|
|
|
/* we want the default implementation */
|
|
parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_table_layout_parent_class);
|
|
|
|
return parent_class->begin_animation (manager, duration, easing);
|
|
}
|
|
|
|
static void
|
|
clutter_table_layout_end_animation (ClutterLayoutManager *manager)
|
|
{
|
|
ClutterTableLayoutPrivate *priv = CLUTTER_TABLE_LAYOUT (manager)->priv;
|
|
ClutterLayoutManagerClass *parent_class;
|
|
|
|
priv->is_animating = FALSE;
|
|
|
|
/* we want the default implementation */
|
|
parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_table_layout_parent_class);
|
|
parent_class->end_animation (manager);
|
|
}
|
|
|
|
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;
|
|
|
|
case PROP_USE_ANIMATIONS:
|
|
clutter_table_layout_set_use_animations (self, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_EASING_MODE:
|
|
clutter_table_layout_set_easing_mode (self, g_value_get_ulong (value));
|
|
break;
|
|
|
|
case PROP_EASING_DURATION:
|
|
clutter_table_layout_set_easing_duration (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;
|
|
|
|
case PROP_USE_ANIMATIONS:
|
|
g_value_set_boolean (value, priv->use_animations);
|
|
break;
|
|
|
|
case PROP_EASING_MODE:
|
|
g_value_set_ulong (value, priv->easing_mode);
|
|
break;
|
|
|
|
case PROP_EASING_DURATION:
|
|
g_value_set_uint (value, priv->easing_duration);
|
|
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;
|
|
layout_class->begin_animation = clutter_table_layout_begin_animation;
|
|
layout_class->end_animation = clutter_table_layout_end_animation;
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterTableLayoutPrivate));
|
|
|
|
/**
|
|
* ClutterTableLayout:column-spacing:
|
|
*
|
|
* The spacing between columns of the #ClutterTableLayout, in pixels
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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);
|
|
|
|
/**
|
|
* ClutterTableLayout:use-animations:
|
|
*
|
|
* Whether the #ClutterTableLayout should animate changes in the
|
|
* layout properties
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_boolean ("use-animations",
|
|
P_("Use Animations"),
|
|
P_("Whether layout changes should be animated"),
|
|
FALSE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_USE_ANIMATIONS, pspec);
|
|
|
|
/**
|
|
* ClutterTableLayout:easing-mode:
|
|
*
|
|
* The easing mode for the animations, in case
|
|
* #ClutterTableLayout:use-animations is set to %TRUE
|
|
*
|
|
* The easing mode has the same semantics of #ClutterAnimation:mode: it can
|
|
* either be a value from the #ClutterAnimationMode enumeration, like
|
|
* %CLUTTER_EASE_OUT_CUBIC, or a logical id as returned by
|
|
* clutter_alpha_register_func()
|
|
*
|
|
* The default value is %CLUTTER_EASE_OUT_CUBIC
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_ulong ("easing-mode",
|
|
P_("Easing Mode"),
|
|
P_("The easing mode of the animations"),
|
|
0, G_MAXULONG,
|
|
CLUTTER_EASE_OUT_CUBIC,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_EASING_MODE, pspec);
|
|
|
|
/**
|
|
* ClutterTableLayout:easing-duration:
|
|
*
|
|
* The duration of the animations, in case #ClutterTableLayout:use-animations
|
|
* is set to %TRUE
|
|
*
|
|
* The duration is expressed in milliseconds
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
pspec = g_param_spec_uint ("easing-duration",
|
|
P_("Easing Duration"),
|
|
P_("The duration of the animations"),
|
|
0, G_MAXUINT,
|
|
500,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class, PROP_EASING_DURATION, 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->use_animations = FALSE;
|
|
priv->easing_mode = CLUTTER_EASE_OUT_CUBIC;
|
|
priv->easing_duration = 500;
|
|
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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);
|
|
|
|
if (priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (manager,
|
|
priv->easing_duration,
|
|
priv->easing_mode);
|
|
}
|
|
else
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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);
|
|
|
|
if (priv->use_animations)
|
|
{
|
|
clutter_layout_manager_begin_animation (manager,
|
|
priv->easing_duration,
|
|
priv->easing_mode);
|
|
}
|
|
else
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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.
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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_container_add_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 + 1;
|
|
|
|
if (column < 0)
|
|
column = priv->n_cols + 1;
|
|
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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()
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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_set_alignment:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @x_align: Horizontal alignment policy for @actor
|
|
* @y_align: Vertical alignment policy for @actor
|
|
*
|
|
* Sets the horizontal and vertical alignment policies for @actor
|
|
* inside @layout
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_set_alignment (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
ClutterTableAlignment x_align,
|
|
ClutterTableAlignment y_align)
|
|
{
|
|
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_align (CLUTTER_TABLE_CHILD (meta), x_align, y_align);
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_alignment:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @x_align: (out): return location for the horizontal alignment policy
|
|
* @y_align: (out): return location for the vertical alignment policy
|
|
*
|
|
* Retrieves the horizontal and vertical alignment policies for @actor
|
|
* as set using clutter_table_layout_pack() or
|
|
* clutter_table_layout_set_alignment().
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_get_alignment (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
ClutterTableAlignment *x_align,
|
|
ClutterTableAlignment *y_align)
|
|
{
|
|
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 (x_align)
|
|
*x_align = CLUTTER_TABLE_CHILD (meta)->x_align;
|
|
|
|
if (y_align)
|
|
*y_align = CLUTTER_TABLE_CHILD (meta)->y_align;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_fill:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @x_fill: whether @actor should fill horizontally the allocated space
|
|
* @y_fill: whether @actor should fill vertically the allocated space
|
|
*
|
|
* Sets the horizontal and vertical fill policies for @actor
|
|
* inside @layout
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_set_fill (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gboolean x_fill,
|
|
gboolean y_fill)
|
|
{
|
|
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_fill (CLUTTER_TABLE_CHILD (meta), x_fill, y_fill);
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_fill:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @x_fill: (out): return location for the horizontal fill policy
|
|
* @y_fill: (out): return location for the vertical fill policy
|
|
*
|
|
* Retrieves the horizontal and vertical fill policies for @actor
|
|
* as set using clutter_table_layout_pack() or clutter_table_layout_set_fill()
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_get_fill (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gboolean *x_fill,
|
|
gboolean *y_fill)
|
|
{
|
|
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 (x_fill)
|
|
*x_fill = CLUTTER_TABLE_CHILD (meta)->x_fill;
|
|
|
|
if (y_fill)
|
|
*y_fill = CLUTTER_TABLE_CHILD (meta)->y_fill;
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_table_layout_set_expand:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @x_expand: whether @actor should allocate extra space horizontally
|
|
* @y_expand: whether @actor should allocate extra space vertically
|
|
*
|
|
* Sets the horizontal and vertical expand policies for @actor
|
|
* inside @layout
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_set_expand (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gboolean x_expand,
|
|
gboolean y_expand)
|
|
{
|
|
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_expand (CLUTTER_TABLE_CHILD (meta), x_expand, y_expand);
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_expand:
|
|
* @layout: a #ClutterTableLayout
|
|
* @actor: a #ClutterActor child of @layout
|
|
* @x_expand: (out): return location for the horizontal expand policy
|
|
* @y_expand: (out): return location for the vertical expand policy
|
|
*
|
|
* Retrieves the horizontal and vertical expand policies for @actor
|
|
* as set using clutter_table_layout_pack() or clutter_table_layout_set_expand()
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_get_expand (ClutterTableLayout *layout,
|
|
ClutterActor *actor,
|
|
gboolean *x_expand,
|
|
gboolean *y_expand)
|
|
{
|
|
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 (x_expand)
|
|
*x_expand = CLUTTER_TABLE_CHILD (meta)->x_expand;
|
|
|
|
if (y_expand)
|
|
*y_expand = CLUTTER_TABLE_CHILD (meta)->y_expand;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_use_animations:
|
|
* @layout: a #ClutterTableLayout
|
|
* @animate: %TRUE if the @layout should use animations
|
|
*
|
|
* Sets whether @layout should animate changes in the layout properties
|
|
*
|
|
* The duration of the animations is controlled by
|
|
* clutter_table_layout_set_easing_duration(); the easing mode to be used
|
|
* by the animations is controlled by clutter_table_layout_set_easing_mode()
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_set_use_animations (ClutterTableLayout *layout,
|
|
gboolean animate)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
|
|
priv = layout->priv;
|
|
|
|
animate = !!animate;
|
|
if (priv->use_animations != animate)
|
|
{
|
|
priv->use_animations = animate;
|
|
|
|
g_object_notify (G_OBJECT (layout), "use-animations");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_use_animations:
|
|
* @layout: a #ClutterTableLayout
|
|
*
|
|
* Retrieves whether @layout should animate changes in the layout properties
|
|
*
|
|
* Since clutter_table_layout_set_use_animations()
|
|
*
|
|
* Return value: %TRUE if the animations should be used, %FALSE otherwise
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gboolean
|
|
clutter_table_layout_get_use_animations (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), FALSE);
|
|
|
|
return layout->priv->use_animations;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_easing_mode:
|
|
* @layout: a #ClutterTableLayout
|
|
* @mode: an easing mode, either from #ClutterAnimationMode or a logical id
|
|
* from clutter_alpha_register_func()
|
|
*
|
|
* Sets the easing mode to be used by @layout when animating changes in layout
|
|
* properties
|
|
*
|
|
* Use clutter_table_layout_set_use_animations() to enable and disable the
|
|
* animations
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_set_easing_mode (ClutterTableLayout *layout,
|
|
gulong mode)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->easing_mode != mode)
|
|
{
|
|
priv->easing_mode = mode;
|
|
|
|
g_object_notify (G_OBJECT (layout), "easing-mode");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_easing_mode:
|
|
* @layout: a #ClutterTableLayout
|
|
*
|
|
* Retrieves the easing mode set using clutter_table_layout_set_easing_mode()
|
|
*
|
|
* Return value: an easing mode
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
gulong
|
|
clutter_table_layout_get_easing_mode (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout),
|
|
CLUTTER_EASE_OUT_CUBIC);
|
|
|
|
return layout->priv->easing_mode;
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_set_easing_duration:
|
|
* @layout: a #ClutterTableLayout
|
|
* @msecs: the duration of the animations, in milliseconds
|
|
*
|
|
* Sets the duration of the animations used by @layout when animating changes
|
|
* in the layout properties
|
|
*
|
|
* Use clutter_table_layout_set_use_animations() to enable and disable the
|
|
* animations
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
void
|
|
clutter_table_layout_set_easing_duration (ClutterTableLayout *layout,
|
|
guint msecs)
|
|
{
|
|
ClutterTableLayoutPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout));
|
|
|
|
priv = layout->priv;
|
|
|
|
if (priv->easing_duration != msecs)
|
|
{
|
|
priv->easing_duration = msecs;
|
|
|
|
g_object_notify (G_OBJECT (layout), "easing-duration");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_table_layout_get_easing_duration:
|
|
* @layout: a #ClutterTableLayout
|
|
*
|
|
* Retrieves the duration set using clutter_table_layout_set_easing_duration()
|
|
*
|
|
* Return value: the duration of the animations, in milliseconds
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
guint
|
|
clutter_table_layout_get_easing_duration (ClutterTableLayout *layout)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TABLE_LAYOUT (layout), 500);
|
|
|
|
return layout->priv->easing_duration;
|
|
}
|
|
|
|
|
|
/**
|
|
* clutter_table_layout_get_row_count:
|
|
* @layout: A #ClutterTableLayout
|
|
*
|
|
* Retrieve the current number rows in the @layout
|
|
*
|
|
* Returns: the number of rows
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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
|
|
*
|
|
* Since: 1.4
|
|
*/
|
|
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;
|
|
}
|