1006 lines
29 KiB
C
1006 lines
29 KiB
C
|
/* tidy-grid.h: Reflowing grid layout container for clutter.
|
||
|
*
|
||
|
* Copyright (C) 2008 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, write to the
|
||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
|
* Boston, MA 02111-1307, USA.
|
||
|
*
|
||
|
* Written by: Øyvind Kolås <pippin@linux.intel.com>
|
||
|
*/
|
||
|
|
||
|
/* TODO:
|
||
|
*
|
||
|
* - Better names for properties.
|
||
|
* - Caching layouted positions? (perhaps needed for huge collections)
|
||
|
* - More comments / overall concept on how the layouting is done.
|
||
|
* - Allow more layout directions than just row major / column major.
|
||
|
*/
|
||
|
|
||
|
#include <clutter/clutter-actor.h>
|
||
|
#include <clutter/clutter-container.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "tidy-grid.h"
|
||
|
|
||
|
typedef struct _TidyGridActorData TidyGridActorData;
|
||
|
|
||
|
static void tidy_grid_dispose (GObject *object);
|
||
|
static void tidy_grid_finalize (GObject *object);
|
||
|
|
||
|
static void tidy_grid_finalize (GObject *object);
|
||
|
|
||
|
static void tidy_grid_set_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec);
|
||
|
static void tidy_grid_get_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec);
|
||
|
|
||
|
static void clutter_container_iface_init (ClutterContainerIface *iface);
|
||
|
|
||
|
static void tidy_grid_real_add (ClutterContainer *container,
|
||
|
ClutterActor *actor);
|
||
|
static void tidy_grid_real_remove (ClutterContainer *container,
|
||
|
ClutterActor *actor);
|
||
|
static void tidy_grid_real_foreach (ClutterContainer *container,
|
||
|
ClutterCallback callback,
|
||
|
gpointer user_data);
|
||
|
static void tidy_grid_real_raise (ClutterContainer *container,
|
||
|
ClutterActor *actor,
|
||
|
ClutterActor *sibling);
|
||
|
static void tidy_grid_real_lower (ClutterContainer *container,
|
||
|
ClutterActor *actor,
|
||
|
ClutterActor *sibling);
|
||
|
static void
|
||
|
tidy_grid_real_sort_depth_order (ClutterContainer *container);
|
||
|
|
||
|
static void
|
||
|
tidy_grid_free_actor_data (gpointer data);
|
||
|
|
||
|
static void tidy_grid_paint (ClutterActor *actor);
|
||
|
|
||
|
static void tidy_grid_pick (ClutterActor *actor,
|
||
|
const ClutterColor *color);
|
||
|
|
||
|
static void
|
||
|
tidy_grid_get_preferred_width (ClutterActor *self,
|
||
|
ClutterUnit for_height,
|
||
|
ClutterUnit *min_width_p,
|
||
|
ClutterUnit *natural_width_p);
|
||
|
|
||
|
static void
|
||
|
tidy_grid_get_preferred_height (ClutterActor *self,
|
||
|
ClutterUnit for_width,
|
||
|
ClutterUnit *min_height_p,
|
||
|
ClutterUnit *natural_height_p);
|
||
|
|
||
|
static void tidy_grid_allocate (ClutterActor *self,
|
||
|
const ClutterActorBox *box,
|
||
|
gboolean absolute_origin_changed);
|
||
|
|
||
|
G_DEFINE_TYPE_WITH_CODE (TidyGrid, tidy_grid,
|
||
|
CLUTTER_TYPE_ACTOR,
|
||
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
|
||
|
clutter_container_iface_init));
|
||
|
|
||
|
#define TIDY_GRID_GET_PRIVATE(obj) \
|
||
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_GRID, \
|
||
|
TidyGridPrivate))
|
||
|
|
||
|
struct _TidyGridPrivate
|
||
|
{
|
||
|
ClutterUnit for_height, for_width;
|
||
|
ClutterUnit pref_width, pref_height;
|
||
|
ClutterUnit alloc_width, alloc_height;
|
||
|
|
||
|
gboolean absolute_origin_changed;
|
||
|
GHashTable *hash_table;
|
||
|
GList *list;
|
||
|
|
||
|
gboolean homogenous_rows;
|
||
|
gboolean homogenous_columns;
|
||
|
gboolean end_align;
|
||
|
ClutterUnit column_gap, row_gap;
|
||
|
gdouble valign, halign;
|
||
|
|
||
|
gboolean column_major;
|
||
|
|
||
|
gboolean first_of_batch;
|
||
|
ClutterUnit a_current_sum, a_wrap;
|
||
|
ClutterUnit max_extent_a;
|
||
|
ClutterUnit max_extent_b;
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
PROP_HOMOGENOUS_ROWS,
|
||
|
PROP_HOMOGENOUS_COLUMNS,
|
||
|
PROP_ROW_GAP,
|
||
|
PROP_COLUMN_GAP,
|
||
|
PROP_VALIGN,
|
||
|
PROP_HALIGN,
|
||
|
PROP_END_ALIGN,
|
||
|
PROP_COLUMN_MAJOR,
|
||
|
};
|
||
|
|
||
|
struct _TidyGridActorData
|
||
|
{
|
||
|
gboolean xpos_set, ypos_set;
|
||
|
ClutterUnit xpos, ypos;
|
||
|
ClutterUnit pref_width, pref_height;
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
tidy_grid_class_init (TidyGridClass *klass)
|
||
|
{
|
||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||
|
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
|
||
|
|
||
|
gobject_class->dispose = tidy_grid_dispose;
|
||
|
gobject_class->finalize = tidy_grid_finalize;
|
||
|
|
||
|
gobject_class->set_property = tidy_grid_set_property;
|
||
|
gobject_class->get_property = tidy_grid_get_property;
|
||
|
|
||
|
actor_class->paint = tidy_grid_paint;
|
||
|
actor_class->pick = tidy_grid_pick;
|
||
|
actor_class->get_preferred_width = tidy_grid_get_preferred_width;
|
||
|
actor_class->get_preferred_height = tidy_grid_get_preferred_height;
|
||
|
actor_class->allocate = tidy_grid_allocate;
|
||
|
|
||
|
g_type_class_add_private (klass, sizeof (TidyGridPrivate));
|
||
|
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_ROW_GAP,
|
||
|
clutter_param_spec_unit ("row-gap",
|
||
|
"Row gap",
|
||
|
"gap between rows in the layout",
|
||
|
0, CLUTTER_MAXUNIT,
|
||
|
0,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_COLUMN_GAP,
|
||
|
clutter_param_spec_unit ("column-gap",
|
||
|
"Column gap",
|
||
|
"gap between columns in the layout",
|
||
|
0, CLUTTER_MAXUNIT,
|
||
|
0,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_HOMOGENOUS_ROWS,
|
||
|
g_param_spec_boolean ("homogenous-rows",
|
||
|
"homogenous rows",
|
||
|
"Should all rows have the same height?",
|
||
|
FALSE,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_HOMOGENOUS_COLUMNS,
|
||
|
g_param_spec_boolean ("homogenous-columns",
|
||
|
"homogenous columns",
|
||
|
"Should all columns have the same height?",
|
||
|
FALSE,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_COLUMN_MAJOR,
|
||
|
g_param_spec_boolean ("column-major",
|
||
|
"column-major",
|
||
|
"Do a column filling first instead of row filling first",
|
||
|
FALSE,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_END_ALIGN,
|
||
|
g_param_spec_boolean ("end-align",
|
||
|
"end-align",
|
||
|
"Right/bottom aligned rows/columns",
|
||
|
FALSE,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_VALIGN,
|
||
|
g_param_spec_double ("valign",
|
||
|
"Vertical align",
|
||
|
"Vertical alignment of items within cells",
|
||
|
0.0, 1.0, 0.0,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
g_object_class_install_property
|
||
|
(gobject_class,
|
||
|
PROP_HALIGN,
|
||
|
g_param_spec_double ("halign",
|
||
|
"Horizontal align",
|
||
|
"Horizontal alignment of items within cells",
|
||
|
0.0, 1.0, 0.0,
|
||
|
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
|
||
|
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
clutter_container_iface_init (ClutterContainerIface *iface)
|
||
|
{
|
||
|
iface->add = tidy_grid_real_add;
|
||
|
iface->remove = tidy_grid_real_remove;
|
||
|
iface->foreach = tidy_grid_real_foreach;
|
||
|
iface->raise = tidy_grid_real_raise;
|
||
|
iface->lower = tidy_grid_real_lower;
|
||
|
iface->sort_depth_order = tidy_grid_real_sort_depth_order;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_init (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv;
|
||
|
|
||
|
self->priv = priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
|
||
|
/* do not unref in the hashtable, the reference is for now kept by the list
|
||
|
* (double bookkeeping sucks)
|
||
|
*/
|
||
|
priv->hash_table
|
||
|
= g_hash_table_new_full (g_direct_hash,
|
||
|
g_direct_equal,
|
||
|
NULL,
|
||
|
tidy_grid_free_actor_data);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_dispose (GObject *object)
|
||
|
{
|
||
|
TidyGrid *self = (TidyGrid *) object;
|
||
|
TidyGridPrivate *priv;
|
||
|
|
||
|
priv = self->priv;
|
||
|
|
||
|
/* Destroy all of the children. This will cause them to be removed
|
||
|
from the container and unparented */
|
||
|
clutter_container_foreach (CLUTTER_CONTAINER (object),
|
||
|
(ClutterCallback) clutter_actor_destroy,
|
||
|
NULL);
|
||
|
|
||
|
G_OBJECT_CLASS (tidy_grid_parent_class)->dispose (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_finalize (GObject *object)
|
||
|
{
|
||
|
TidyGrid *self = (TidyGrid *) object;
|
||
|
TidyGridPrivate *priv = self->priv;
|
||
|
|
||
|
g_hash_table_destroy (priv->hash_table);
|
||
|
|
||
|
G_OBJECT_CLASS (tidy_grid_parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_end_align (TidyGrid *self,
|
||
|
gboolean value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->end_align = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
tidy_grid_get_end_align (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->end_align;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_homogenous_rows (TidyGrid *self,
|
||
|
gboolean value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->homogenous_rows = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
tidy_grid_get_homogenous_rows (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->homogenous_rows;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_homogenous_columns (TidyGrid *self,
|
||
|
gboolean value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->homogenous_columns = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
|
||
|
gboolean
|
||
|
tidy_grid_get_homogenous_columns (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->homogenous_columns;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_column_major (TidyGrid *self,
|
||
|
gboolean value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->column_major = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
gboolean
|
||
|
tidy_grid_get_column_major (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->column_major;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_column_gap (TidyGrid *self,
|
||
|
ClutterUnit value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->column_gap = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
ClutterUnit
|
||
|
tidy_grid_get_column_gap (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->column_gap;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_row_gap (TidyGrid *self,
|
||
|
ClutterUnit value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->row_gap = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
ClutterUnit
|
||
|
tidy_grid_get_row_gap (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->row_gap;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_valign (TidyGrid *self,
|
||
|
gdouble value)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->valign = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
gdouble
|
||
|
tidy_grid_get_valign (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->valign;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
tidy_grid_set_halign (TidyGrid *self,
|
||
|
gdouble value)
|
||
|
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
priv->halign = value;
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||
|
}
|
||
|
|
||
|
gdouble
|
||
|
tidy_grid_get_halign (TidyGrid *self)
|
||
|
{
|
||
|
TidyGridPrivate *priv = TIDY_GRID_GET_PRIVATE (self);
|
||
|
return priv->halign;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
tidy_grid_set_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
TidyGrid *grid = TIDY_GRID (object);
|
||
|
|
||
|
TidyGridPrivate *priv;
|
||
|
|
||
|
priv = TIDY_GRID_GET_PRIVATE (object);
|
||
|
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
case PROP_END_ALIGN:
|
||
|
tidy_grid_set_end_align (grid, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_HOMOGENOUS_ROWS:
|
||
|
tidy_grid_set_homogenous_rows (grid, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_HOMOGENOUS_COLUMNS:
|
||
|
tidy_grid_set_homogenous_columns (grid, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_COLUMN_MAJOR:
|
||
|
tidy_grid_set_column_major (grid, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_COLUMN_GAP:
|
||
|
tidy_grid_set_column_gap (grid, clutter_value_get_unit (value));
|
||
|
break;
|
||
|
case PROP_ROW_GAP:
|
||
|
tidy_grid_set_row_gap (grid, clutter_value_get_unit (value));
|
||
|
break;
|
||
|
case PROP_VALIGN:
|
||
|
tidy_grid_set_valign (grid, g_value_get_double (value));
|
||
|
break;
|
||
|
case PROP_HALIGN:
|
||
|
tidy_grid_set_halign (grid, g_value_get_double (value));
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_get_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
TidyGrid *grid = TIDY_GRID (object);
|
||
|
|
||
|
TidyGridPrivate *priv;
|
||
|
|
||
|
priv = TIDY_GRID_GET_PRIVATE (object);
|
||
|
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
case PROP_HOMOGENOUS_ROWS:
|
||
|
g_value_set_boolean (value, tidy_grid_get_homogenous_rows (grid));
|
||
|
break;
|
||
|
case PROP_HOMOGENOUS_COLUMNS:
|
||
|
g_value_set_boolean (value, tidy_grid_get_homogenous_columns (grid));
|
||
|
break;
|
||
|
case PROP_END_ALIGN:
|
||
|
g_value_set_boolean (value, tidy_grid_get_end_align (grid));
|
||
|
break;
|
||
|
case PROP_COLUMN_MAJOR:
|
||
|
g_value_set_boolean (value, tidy_grid_get_column_major (grid));
|
||
|
break;
|
||
|
case PROP_COLUMN_GAP:
|
||
|
clutter_value_set_unit (value, tidy_grid_get_column_gap (grid));
|
||
|
break;
|
||
|
case PROP_ROW_GAP:
|
||
|
clutter_value_set_unit (value, tidy_grid_get_row_gap (grid));
|
||
|
break;
|
||
|
case PROP_VALIGN:
|
||
|
g_value_set_double (value, tidy_grid_get_valign (grid));
|
||
|
break;
|
||
|
case PROP_HALIGN:
|
||
|
g_value_set_double (value, tidy_grid_get_halign (grid));
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
tidy_grid_free_actor_data (gpointer data)
|
||
|
{
|
||
|
g_slice_free (TidyGridActorData, data);
|
||
|
}
|
||
|
|
||
|
ClutterActor *
|
||
|
tidy_grid_new (void)
|
||
|
{
|
||
|
ClutterActor *self = g_object_new (TIDY_TYPE_GRID, NULL);
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_real_add (ClutterContainer *container,
|
||
|
ClutterActor *actor)
|
||
|
{
|
||
|
TidyGridPrivate *priv;
|
||
|
TidyGridActorData *data;
|
||
|
|
||
|
g_return_if_fail (TIDY_IS_GRID (container));
|
||
|
|
||
|
priv = TIDY_GRID (container)->priv;
|
||
|
|
||
|
g_object_ref (actor);
|
||
|
|
||
|
clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
|
||
|
|
||
|
data = g_slice_alloc0 (sizeof (TidyGridActorData));
|
||
|
|
||
|
priv->list = g_list_append (priv->list, actor);
|
||
|
g_hash_table_insert (priv->hash_table, actor, data);
|
||
|
|
||
|
g_signal_emit_by_name (container, "actor-added", actor);
|
||
|
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
|
||
|
|
||
|
g_object_unref (actor);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_real_remove (ClutterContainer *container,
|
||
|
ClutterActor *actor)
|
||
|
{
|
||
|
TidyGrid *layout = TIDY_GRID (container);
|
||
|
TidyGridPrivate *priv = layout->priv;
|
||
|
|
||
|
g_object_ref (actor);
|
||
|
|
||
|
if (g_hash_table_remove (priv->hash_table, actor))
|
||
|
{
|
||
|
clutter_actor_unparent (actor);
|
||
|
|
||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (layout));
|
||
|
|
||
|
g_signal_emit_by_name (container, "actor-removed", actor);
|
||
|
|
||
|
if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (layout)))
|
||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (layout));
|
||
|
}
|
||
|
priv->list = g_list_remove (priv->list, actor);
|
||
|
|
||
|
g_object_unref (actor);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_real_foreach (ClutterContainer *container,
|
||
|
ClutterCallback callback,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
TidyGrid *layout = TIDY_GRID (container);
|
||
|
TidyGridPrivate *priv = layout->priv;
|
||
|
|
||
|
g_list_foreach (priv->list, (GFunc) callback, user_data);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_real_raise (ClutterContainer *container,
|
||
|
ClutterActor *actor,
|
||
|
ClutterActor *sibling)
|
||
|
{
|
||
|
/* STUB */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_real_lower (ClutterContainer *container,
|
||
|
ClutterActor *actor,
|
||
|
ClutterActor *sibling)
|
||
|
{
|
||
|
/* STUB */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_real_sort_depth_order (ClutterContainer *container)
|
||
|
{
|
||
|
/* STUB */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_paint (ClutterActor *actor)
|
||
|
{
|
||
|
TidyGrid *layout = (TidyGrid *) actor;
|
||
|
TidyGridPrivate *priv = layout->priv;
|
||
|
GList *child_item;
|
||
|
|
||
|
for (child_item = priv->list;
|
||
|
child_item != NULL;
|
||
|
child_item = child_item->next)
|
||
|
{
|
||
|
ClutterActor *child = child_item->data;
|
||
|
|
||
|
g_assert (child != NULL);
|
||
|
|
||
|
if (CLUTTER_ACTOR_IS_VISIBLE (child))
|
||
|
clutter_actor_paint (child);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_pick (ClutterActor *actor,
|
||
|
const ClutterColor *color)
|
||
|
{
|
||
|
/* Chain up so we get a bounding box pained (if we are reactive) */
|
||
|
CLUTTER_ACTOR_CLASS (tidy_grid_parent_class)->pick (actor, color);
|
||
|
|
||
|
/* Just forward to the paint call which in turn will trigger
|
||
|
* the child actors also getting 'picked'.
|
||
|
*/
|
||
|
if (CLUTTER_ACTOR_IS_VISIBLE (actor))
|
||
|
tidy_grid_paint (actor);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_get_preferred_width (ClutterActor *self,
|
||
|
ClutterUnit for_height,
|
||
|
ClutterUnit *min_width_p,
|
||
|
ClutterUnit *natural_width_p)
|
||
|
{
|
||
|
TidyGrid *layout = (TidyGrid *) self;
|
||
|
TidyGridPrivate *priv = layout->priv;
|
||
|
ClutterUnit natural_width;
|
||
|
|
||
|
natural_width = CLUTTER_UNITS_FROM_INT (200);
|
||
|
if (min_width_p)
|
||
|
*min_width_p = natural_width;
|
||
|
if (natural_width_p)
|
||
|
*natural_width_p = natural_width;
|
||
|
|
||
|
priv->pref_width = natural_width;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_get_preferred_height (ClutterActor *self,
|
||
|
ClutterUnit for_width,
|
||
|
ClutterUnit *min_height_p,
|
||
|
ClutterUnit *natural_height_p)
|
||
|
{
|
||
|
TidyGrid *layout = (TidyGrid *) self;
|
||
|
TidyGridPrivate *priv = layout->priv;
|
||
|
ClutterUnit natural_height;
|
||
|
|
||
|
natural_height = CLUTTER_UNITS_FROM_INT (200);
|
||
|
|
||
|
priv->for_width = for_width;
|
||
|
priv->pref_height = natural_height;
|
||
|
|
||
|
if (min_height_p)
|
||
|
*min_height_p = natural_height;
|
||
|
if (natural_height_p)
|
||
|
*natural_height_p = natural_height;
|
||
|
}
|
||
|
|
||
|
static ClutterUnit
|
||
|
compute_row_height (GList *siblings,
|
||
|
ClutterUnit best_yet,
|
||
|
ClutterUnit current_a,
|
||
|
TidyGridPrivate *priv)
|
||
|
{
|
||
|
GList *l;
|
||
|
|
||
|
gboolean homogenous_a;
|
||
|
gboolean homogenous_b;
|
||
|
ClutterUnit gap;
|
||
|
|
||
|
if (priv->column_major)
|
||
|
{
|
||
|
homogenous_b = priv->homogenous_columns;
|
||
|
homogenous_a = priv->homogenous_rows;
|
||
|
gap = priv->row_gap;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
homogenous_a = priv->homogenous_columns;
|
||
|
homogenous_b = priv->homogenous_rows;
|
||
|
gap = priv->column_gap;
|
||
|
}
|
||
|
|
||
|
for (l = siblings; l != NULL; l = l->next)
|
||
|
{
|
||
|
ClutterActor *child = l->data;
|
||
|
ClutterUnit natural_width, natural_height;
|
||
|
|
||
|
/* each child will get as much space as they require */
|
||
|
clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
|
||
|
NULL, NULL,
|
||
|
&natural_width, &natural_height);
|
||
|
|
||
|
if (priv->column_major)
|
||
|
{
|
||
|
ClutterUnit temp = natural_height;
|
||
|
natural_height = natural_width;
|
||
|
natural_width = temp;
|
||
|
}
|
||
|
|
||
|
/* if the primary axis is homogenous, each additional item is the same
|
||
|
* width */
|
||
|
if (homogenous_a)
|
||
|
natural_width = priv->max_extent_a;
|
||
|
|
||
|
if (natural_height > best_yet)
|
||
|
best_yet = natural_height;
|
||
|
|
||
|
/* if the child is overflowing, we wrap to next line */
|
||
|
if (current_a + natural_width + gap > priv->a_wrap)
|
||
|
{
|
||
|
return best_yet;
|
||
|
}
|
||
|
current_a += natural_width + gap;
|
||
|
}
|
||
|
return best_yet;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
static ClutterUnit
|
||
|
compute_row_start (GList *siblings,
|
||
|
ClutterUnit start_x,
|
||
|
TidyGridPrivate *priv)
|
||
|
{
|
||
|
ClutterUnit current_a = start_x;
|
||
|
GList *l;
|
||
|
|
||
|
gboolean homogenous_a;
|
||
|
gboolean homogenous_b;
|
||
|
ClutterUnit gap;
|
||
|
|
||
|
if (priv->column_major)
|
||
|
{
|
||
|
homogenous_b = priv->homogenous_columns;
|
||
|
homogenous_a = priv->homogenous_rows;
|
||
|
gap = priv->row_gap;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
homogenous_a = priv->homogenous_columns;
|
||
|
homogenous_b = priv->homogenous_rows;
|
||
|
gap = priv->column_gap;
|
||
|
}
|
||
|
|
||
|
for (l = siblings; l != NULL; l = l->next)
|
||
|
{
|
||
|
ClutterActor *child = l->data;
|
||
|
ClutterUnit natural_width, natural_height;
|
||
|
|
||
|
/* each child will get as much space as they require */
|
||
|
clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
|
||
|
NULL, NULL,
|
||
|
&natural_width, &natural_height);
|
||
|
|
||
|
|
||
|
if (priv->column_major)
|
||
|
natural_width = natural_height;
|
||
|
|
||
|
/* if the primary axis is homogenous, each additional item is the same width */
|
||
|
if (homogenous_a)
|
||
|
natural_width = priv->max_extent_a;
|
||
|
|
||
|
/* if the child is overflowing, we wrap to next line */
|
||
|
if (current_a + natural_width + gap > priv->a_wrap)
|
||
|
{
|
||
|
if (current_a == start_x)
|
||
|
return start_x;
|
||
|
return (priv->a_wrap - current_a);
|
||
|
}
|
||
|
current_a += natural_width + gap;
|
||
|
}
|
||
|
return (priv->a_wrap - current_a);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
tidy_grid_allocate (ClutterActor *self,
|
||
|
const ClutterActorBox *box,
|
||
|
gboolean absolute_origin_changed)
|
||
|
{
|
||
|
TidyGrid *layout = (TidyGrid *) self;
|
||
|
TidyGridPrivate *priv = layout->priv;
|
||
|
|
||
|
ClutterUnit current_a;
|
||
|
ClutterUnit current_b;
|
||
|
ClutterUnit next_b;
|
||
|
ClutterUnit agap;
|
||
|
ClutterUnit bgap;
|
||
|
|
||
|
gboolean homogenous_a;
|
||
|
gboolean homogenous_b;
|
||
|
gdouble aalign;
|
||
|
gdouble balign;
|
||
|
|
||
|
current_a = current_b = next_b = 0;
|
||
|
|
||
|
GList *iter;
|
||
|
|
||
|
/* chain up to set actor->allocation */
|
||
|
CLUTTER_ACTOR_CLASS (tidy_grid_parent_class)
|
||
|
->allocate (self, box, absolute_origin_changed);
|
||
|
|
||
|
priv->alloc_width = box->x2 - box->x1;
|
||
|
priv->alloc_height = box->y2 - box->y1;
|
||
|
priv->absolute_origin_changed = absolute_origin_changed;
|
||
|
|
||
|
/* Make sure we have calculated the preferred size */
|
||
|
/* what does this do? */
|
||
|
clutter_actor_get_preferred_size (self, NULL, NULL, NULL, NULL);
|
||
|
|
||
|
|
||
|
if (priv->column_major)
|
||
|
{
|
||
|
priv->a_wrap = priv->alloc_height;
|
||
|
homogenous_b = priv->homogenous_columns;
|
||
|
homogenous_a = priv->homogenous_rows;
|
||
|
aalign = priv->valign;
|
||
|
balign = priv->halign;
|
||
|
agap = priv->row_gap;
|
||
|
bgap = priv->column_gap;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
priv->a_wrap = priv->alloc_width;
|
||
|
homogenous_a = priv->homogenous_columns;
|
||
|
homogenous_b = priv->homogenous_rows;
|
||
|
aalign = priv->halign;
|
||
|
balign = priv->valign;
|
||
|
agap = priv->column_gap;
|
||
|
bgap = priv->row_gap;
|
||
|
}
|
||
|
|
||
|
priv->max_extent_a = 0;
|
||
|
priv->max_extent_b = 0;
|
||
|
|
||
|
priv->first_of_batch = TRUE;
|
||
|
|
||
|
if (homogenous_a ||
|
||
|
homogenous_b)
|
||
|
{
|
||
|
for (iter = priv->list; iter; iter = iter->next)
|
||
|
{
|
||
|
ClutterActor *child = iter->data;
|
||
|
ClutterUnit natural_width;
|
||
|
ClutterUnit natural_height;
|
||
|
|
||
|
/* each child will get as much space as they require */
|
||
|
clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
|
||
|
NULL, NULL,
|
||
|
&natural_width, &natural_height);
|
||
|
if (natural_width > priv->max_extent_a)
|
||
|
priv->max_extent_a = natural_width;
|
||
|
if (natural_height > priv->max_extent_b)
|
||
|
priv->max_extent_b = natural_width;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (priv->column_major)
|
||
|
{
|
||
|
ClutterUnit temp = priv->max_extent_a;
|
||
|
priv->max_extent_a = priv->max_extent_b;
|
||
|
priv->max_extent_b = temp;
|
||
|
}
|
||
|
|
||
|
for (iter = priv->list; iter; iter=iter->next)
|
||
|
{
|
||
|
ClutterActor *child = iter->data;
|
||
|
ClutterUnit natural_a;
|
||
|
ClutterUnit natural_b;
|
||
|
|
||
|
/* each child will get as much space as they require */
|
||
|
clutter_actor_get_preferred_size (CLUTTER_ACTOR (child),
|
||
|
NULL, NULL,
|
||
|
&natural_a, &natural_b);
|
||
|
|
||
|
if (priv->column_major) /* swap axes around if column is major */
|
||
|
{
|
||
|
ClutterUnit temp = natural_a;
|
||
|
natural_a = natural_b;
|
||
|
natural_b = temp;
|
||
|
}
|
||
|
|
||
|
/* if the child is overflowing, we wrap to next line */
|
||
|
if (current_a + natural_a > priv->a_wrap ||
|
||
|
(homogenous_a && current_a + priv->max_extent_a > priv->a_wrap))
|
||
|
{
|
||
|
current_b = next_b + bgap;
|
||
|
current_a = 0;
|
||
|
next_b = current_b + bgap;
|
||
|
priv->first_of_batch = TRUE;
|
||
|
}
|
||
|
|
||
|
if (priv->end_align &&
|
||
|
priv->first_of_batch)
|
||
|
{
|
||
|
current_a = compute_row_start (iter, current_a, priv);
|
||
|
priv->first_of_batch = FALSE;
|
||
|
}
|
||
|
|
||
|
if (next_b-current_b < natural_b)
|
||
|
next_b = current_b + natural_b;
|
||
|
|
||
|
{
|
||
|
ClutterUnit row_height;
|
||
|
ClutterActorBox child_box;
|
||
|
|
||
|
if (homogenous_b)
|
||
|
{
|
||
|
row_height = priv->max_extent_b;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
row_height = compute_row_height (iter, next_b-current_b,
|
||
|
current_a, priv);
|
||
|
}
|
||
|
|
||
|
if (homogenous_a)
|
||
|
{
|
||
|
child_box.x1 = current_a + (priv->max_extent_a-natural_a) * aalign;
|
||
|
child_box.x2 = child_box.x1 + natural_a;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
child_box.x1 = current_a;
|
||
|
child_box.x2 = child_box.x1 + natural_a;
|
||
|
}
|
||
|
|
||
|
child_box.y1 = current_b + (row_height-natural_b) * balign;
|
||
|
child_box.y2 = child_box.y1 + natural_b;
|
||
|
|
||
|
|
||
|
if (priv->column_major)
|
||
|
{
|
||
|
ClutterUnit temp = child_box.x1;
|
||
|
child_box.x1 = child_box.y1;
|
||
|
child_box.y1 = temp;
|
||
|
|
||
|
temp = child_box.x2;
|
||
|
child_box.x2 = child_box.y2;
|
||
|
child_box.y2 = temp;
|
||
|
}
|
||
|
|
||
|
/* update the allocation */
|
||
|
clutter_actor_allocate (CLUTTER_ACTOR (child),
|
||
|
&child_box,
|
||
|
absolute_origin_changed);
|
||
|
|
||
|
if (homogenous_a)
|
||
|
{
|
||
|
current_a += priv->max_extent_a + agap;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
current_a += natural_a + agap;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|