54b77d12cd
ClutterBox is meant to work like the HTML boxing model: it has a margin and a backgrdound color, and every child actor is added with a pack type and a padding. The ClutterBoxChild structure holds the child actor, the padding, the packing type and the allocated coordinates for the actor.
684 lines
16 KiB
C
684 lines
16 KiB
C
#include "config.h"
|
|
|
|
#include "cogl.h"
|
|
|
|
#include "clutter-box.h"
|
|
#include "clutter-container.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-main.h"
|
|
#include "clutter-private.h"
|
|
|
|
/**
|
|
* SECTION:clutter-box
|
|
* @short_description: Base class for layout containers
|
|
*
|
|
* FIXME
|
|
*
|
|
* #ClutterBox is available since Clutter 0.4
|
|
*/
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_MARGIN,
|
|
PROP_COLOR
|
|
};
|
|
|
|
static void clutter_container_iface_init (ClutterContainerIface *iface);
|
|
|
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterBox,
|
|
clutter_box,
|
|
CLUTTER_TYPE_ACTOR,
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
|
|
clutter_container_iface_init));
|
|
|
|
|
|
static void
|
|
clutter_box_add (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
clutter_box_pack_defaults (CLUTTER_BOX (container), actor);
|
|
}
|
|
|
|
static void
|
|
clutter_box_remove (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (container);
|
|
GList *l;
|
|
|
|
g_object_ref (actor);
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
if (child->actor == actor)
|
|
{
|
|
CLUTTER_BOX_GET_CLASS (box)->unpack_child (box, child);
|
|
|
|
clutter_actor_unparent (actor);
|
|
|
|
box->children = g_list_remove_link (box->children, l);
|
|
g_list_free (l);
|
|
g_slice_free (ClutterBoxChild, child);
|
|
|
|
g_signal_emit_by_name (container, "actor-removed", actor);
|
|
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (box)))
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_object_unref (actor);
|
|
}
|
|
|
|
static void
|
|
clutter_box_foreach (ClutterContainer *container,
|
|
ClutterCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (container);
|
|
GList *l;
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
if (child->pack_type == CLUTTER_PACK_START)
|
|
(* callback) (child->actor, user_data);
|
|
}
|
|
|
|
for (l = g_list_last (box->children); l; l = l->prev)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
if (child->pack_type == CLUTTER_PACK_END)
|
|
(* callback) (child->actor, user_data);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_container_iface_init (ClutterContainerIface *iface)
|
|
{
|
|
iface->add = clutter_box_add;
|
|
iface->remove = clutter_box_remove;
|
|
iface->foreach = clutter_box_foreach;
|
|
}
|
|
|
|
static void
|
|
clutter_box_show_all (ClutterActor *actor)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (actor);
|
|
GList *l;
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
clutter_actor_show (child->actor);
|
|
}
|
|
|
|
clutter_actor_show (actor);
|
|
}
|
|
|
|
static void
|
|
clutter_box_hide_all (ClutterActor *actor)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (actor);
|
|
GList *l;
|
|
|
|
clutter_actor_hide (actor);
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
clutter_actor_hide (child->actor);
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_box_paint (ClutterActor *actor)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (actor);
|
|
GList *l;
|
|
|
|
cogl_push_matrix ();
|
|
|
|
cogl_color (&box->color);
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
if (CLUTTER_ACTOR_IS_MAPPED (child->actor))
|
|
clutter_actor_paint (child->actor);
|
|
}
|
|
|
|
cogl_pop_matrix ();
|
|
}
|
|
|
|
static void
|
|
clutter_box_pick (ClutterActor *actor,
|
|
const ClutterColor *color)
|
|
{
|
|
/* just repaint; in the future we might enter in a "focused" status here */
|
|
clutter_box_paint (actor);
|
|
}
|
|
|
|
static void
|
|
clutter_box_dispose (GObject *gobject)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (gobject);
|
|
GList *l;
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *child = l->data;
|
|
|
|
clutter_actor_destroy (child->actor);
|
|
g_slice_free (ClutterBoxChild, child);
|
|
}
|
|
|
|
g_list_free (box->children);
|
|
box->children = NULL;
|
|
|
|
G_OBJECT_CLASS (clutter_box_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
clutter_box_pack_child_unimplemented (ClutterBox *box,
|
|
ClutterBoxChild *child)
|
|
{
|
|
g_warning ("ClutterBox of type `%s' does not implement the "
|
|
"ClutterBox::pack_child method.",
|
|
g_type_name (G_OBJECT_TYPE (box)));
|
|
}
|
|
|
|
static void
|
|
clutter_box_unpack_child_unimplemented (ClutterBox *box,
|
|
ClutterBoxChild *child)
|
|
{
|
|
g_warning ("ClutterBox of type `%s' does not implement the "
|
|
"ClutterBox::unpack_child method.",
|
|
g_type_name (G_OBJECT_TYPE (box)));
|
|
}
|
|
|
|
static void
|
|
clutter_box_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_COLOR:
|
|
clutter_box_set_color (box, g_value_get_boxed (value));
|
|
break;
|
|
case PROP_MARGIN:
|
|
clutter_box_set_margin (box, g_value_get_boxed (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_box_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterBox *box = CLUTTER_BOX (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MARGIN:
|
|
{
|
|
ClutterMargin margin;
|
|
clutter_box_get_margin (box, &margin);
|
|
g_value_set_boxed (value, &margin);
|
|
}
|
|
break;
|
|
case PROP_COLOR:
|
|
{
|
|
ClutterColor color;
|
|
clutter_box_get_color (box, &color);
|
|
g_value_set_boxed (value, &color);
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_box_class_init (ClutterBoxClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
|
|
gobject_class->set_property = clutter_box_set_property;
|
|
gobject_class->get_property = clutter_box_get_property;
|
|
gobject_class->dispose = clutter_box_dispose;
|
|
|
|
actor_class->show_all = clutter_box_show_all;
|
|
actor_class->hide_all = clutter_box_hide_all;
|
|
actor_class->paint = clutter_box_paint;
|
|
actor_class->pick = clutter_box_pick;
|
|
|
|
klass->pack_child = clutter_box_pack_child_unimplemented;
|
|
klass->unpack_child = clutter_box_unpack_child_unimplemented;
|
|
|
|
/**
|
|
* ClutterBox:margin:
|
|
*
|
|
* The margin between the inner border of a #ClutterBox and its
|
|
* children.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_MARGIN,
|
|
g_param_spec_boxed ("margin",
|
|
"Margin",
|
|
"Margin between the inner border of a box and its children",
|
|
CLUTTER_TYPE_MARGIN,
|
|
CLUTTER_PARAM_READWRITE));
|
|
/**
|
|
* ClutterBox:color:
|
|
*
|
|
* The background color of a #ClutterBox.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_COLOR,
|
|
g_param_spec_boxed ("color",
|
|
"Color",
|
|
"Background color of a box",
|
|
CLUTTER_TYPE_COLOR,
|
|
CLUTTER_PARAM_READWRITE));
|
|
}
|
|
|
|
static void
|
|
clutter_box_init (ClutterBox *box)
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
* Public API
|
|
*/
|
|
|
|
/**
|
|
* clutter_box_pack:
|
|
* @box: a #ClutterBox
|
|
* @actor: a #ClutterActor
|
|
*
|
|
* Packs @actor into @box.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_pack (ClutterBox *box,
|
|
ClutterActor *actor,
|
|
ClutterPackType pack_type,
|
|
const ClutterPadding *padding)
|
|
{
|
|
ClutterBoxChild *child;
|
|
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
child = g_slice_new (ClutterBoxChild);
|
|
child->actor = actor;
|
|
child->pack_type = pack_type;
|
|
memcpy (&(child->padding), padding, sizeof (ClutterPadding));
|
|
|
|
CLUTTER_BOX_GET_CLASS (box)->pack_child (box, child);
|
|
|
|
box->children = g_list_prepend (box->children, child);
|
|
clutter_actor_set_parent (actor, CLUTTER_ACTOR (box));
|
|
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (box)))
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
|
|
}
|
|
|
|
/**
|
|
* clutter_box_pack_defaults:
|
|
* @box: a #ClutterBox
|
|
* @actor: a #ClutterActor
|
|
*
|
|
* Packs @actor into @box, using the default settings for the
|
|
* pack type and padding.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_pack_defaults (ClutterBox *box,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterPadding padding = { 0, };
|
|
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
clutter_box_pack (box, actor, CLUTTER_PACK_START, &padding);
|
|
}
|
|
|
|
/**
|
|
* clutter_box_query_child:
|
|
* @box: a #ClutterBox
|
|
* @actor: child to query
|
|
* @child: return location for a #ClutterBoxChild or %NULL
|
|
*
|
|
* Queries @box for the packing data of @actor.
|
|
*
|
|
* Return value: %TRUE if @actor is a child of @box
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
gboolean
|
|
clutter_box_query_child (ClutterBox *box,
|
|
ClutterActor *actor,
|
|
ClutterBoxChild *child)
|
|
{
|
|
GList *l;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_BOX (box), FALSE);
|
|
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
|
|
|
|
for (l = box->children; l; l = l->next)
|
|
{
|
|
ClutterBoxChild *box_child = l->data;
|
|
|
|
if (box_child->actor == actor)
|
|
{
|
|
if (child)
|
|
{
|
|
child->actor = actor;
|
|
|
|
child->pack_type = box_child->pack_type;
|
|
|
|
child->child_coords.x1 = box_child->child_coords.x1;
|
|
child->child_coords.y1 = box_child->child_coords.y1;
|
|
child->child_coords.x2 = box_child->child_coords.x2;
|
|
child->child_coords.y2 = box_child->child_coords.y2;
|
|
|
|
child->padding.top = box_child->padding.top;
|
|
child->padding.right = box_child->padding.right;
|
|
child->padding.bottom = box_child->padding.bottom;
|
|
child->padding.left = box_child->padding.left;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* clutter_box_query_nth_child:
|
|
* @box: a #ClutterBox
|
|
* @index_: position of the child
|
|
* @child: return value for a #ClutterBoxChild, or %NULL
|
|
*
|
|
* Queries the child of @box at @index_ and puts the packing informations
|
|
* inside @child.
|
|
*
|
|
* Return value: %TRUE if an actor was found at @index_
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
gboolean
|
|
clutter_box_query_nth_child (ClutterBox *box,
|
|
gint index_,
|
|
ClutterBoxChild *child)
|
|
{
|
|
ClutterBoxChild *box_child;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_BOX (box), FALSE);
|
|
g_return_val_if_fail (index > 0, FALSE);
|
|
|
|
box_child = g_list_nth_data (box->children, index_);
|
|
if (!box_child)
|
|
return FALSE;
|
|
|
|
if (child)
|
|
{
|
|
child->actor = box_child->actor;
|
|
|
|
child->pack_type = box_child->pack_type;
|
|
|
|
child->child_coords.x1 = box_child->child_coords.x1;
|
|
child->child_coords.y1 = box_child->child_coords.y1;
|
|
child->child_coords.x2 = box_child->child_coords.x2;
|
|
child->child_coords.y2 = box_child->child_coords.y2;
|
|
|
|
child->padding.top = box_child->padding.top;
|
|
child->padding.right = box_child->padding.right;
|
|
child->padding.bottom = box_child->padding.bottom;
|
|
child->padding.left = box_child->padding.left;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* clutter_box_get_margin:
|
|
* @box: a #ClutterBox
|
|
* @margin: return location for a #ClutterMargin
|
|
*
|
|
* Gets the value set using clutter_box_set_margin().
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_get_margin (ClutterBox *box,
|
|
ClutterMargin *margin)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
g_return_if_fail (margin != NULL);
|
|
|
|
margin->top = box->margin.top;
|
|
margin->right = box->margin.right;
|
|
margin->bottom = box->margin.bottom;
|
|
margin->left = box->margin.left;
|
|
}
|
|
|
|
/**
|
|
* clutter_box_set_margin:
|
|
* @box: a #ClutterBox
|
|
* @margin: a #ClutterMargin, or %NULL to unset the margin
|
|
*
|
|
* Sets the margin, in #ClutterUnit<!-- -->s, between the inner border
|
|
* of the box and the children of the box.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_set_spacing (ClutterBox *box,
|
|
const ClutterMargin *margin)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
|
|
if (margin)
|
|
{
|
|
box->margin.top = margin->top;
|
|
box->margin.right = margin->right;
|
|
box->margin.bottom = margin->bottom;
|
|
box->margin.left = margin->left;
|
|
}
|
|
else
|
|
{
|
|
box->margin.top = 0;
|
|
box->margin.right = 0;
|
|
box->margin.bottom = 0;
|
|
box->margin.left = 0;
|
|
}
|
|
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (box)))
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
|
|
|
|
g_object_notify (G_OBJECT (box), "margin");
|
|
}
|
|
|
|
/**
|
|
* clutter_box_get_color:
|
|
* @box: a #ClutterBox
|
|
* @color: return location for the color
|
|
*
|
|
* Gets the background color of the box set with clutter_box_set_color().
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_get_color (ClutterBox *box,
|
|
ClutterColor *color)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
g_return_if_fail (color != NULL);
|
|
|
|
color->red = box->color.red;
|
|
color->green = box->color.green;
|
|
color->blue = box->color.blue;
|
|
color->alpha = box->color.alpha;
|
|
}
|
|
|
|
/**
|
|
* clutter_box_set_color:
|
|
* @box: a #ClutterBox
|
|
* @color: the background color of the box
|
|
*
|
|
* Sets the background color of the box.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_set_color (ClutterBox *box,
|
|
const ClutterColor *color)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
g_return_if_fail (color != NULL);
|
|
|
|
box->color.red = color->red;
|
|
box->color.green = color->green;
|
|
box->color.blue = color->blue;
|
|
box->color.alpha = color->alpha;
|
|
|
|
if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (box)))
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
|
|
|
|
g_object_notify (G_OBJECT (box), "color");
|
|
}
|
|
|
|
/**
|
|
* clutter_box_remove_all:
|
|
* @box: a #ClutterBox
|
|
*
|
|
* Removes all children actors from the #ClutterBox
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_box_remove_all (ClutterBox *box)
|
|
{
|
|
GList *children;
|
|
|
|
g_return_if_fail (CLUTTER_IS_BOX (box));
|
|
|
|
children = box->children;
|
|
while (children)
|
|
{
|
|
ClutterBoxChild *child = children->data;
|
|
children = children->next;
|
|
|
|
clutter_container_remove_actor (CLUTTER_CONTAINER (box), child->actor);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Boxed types
|
|
*/
|
|
|
|
static void
|
|
clutter_margin_free (ClutterMargin *margin)
|
|
{
|
|
if (G_LIKELY (margin))
|
|
{
|
|
g_slice_free (ClutterMargin, margin);
|
|
}
|
|
}
|
|
|
|
static ClutterMargin *
|
|
clutter_margin_copy (const ClutterMargin *margin)
|
|
{
|
|
ClutterMargin *copy;
|
|
|
|
g_return_val_if_fail (margin != NULL, NULL);
|
|
|
|
copy = g_slice_new (ClutterMargin);
|
|
*copy = *margin;
|
|
|
|
return copy;
|
|
}
|
|
|
|
GType
|
|
clutter_margin_get_type (void)
|
|
{
|
|
static GType gtype = 0;
|
|
|
|
if (G_UNLIKELY (gtype == 0))
|
|
gtype = g_boxed_type_register_static (g_intern_static_string ("ClutterMargin"),
|
|
(GBoxedCopyFunc) clutter_margin_copy,
|
|
(GBoxedFreeFunc) clutter_margin_free);
|
|
|
|
return gtype;
|
|
}
|
|
|
|
static void
|
|
clutter_padding_free (ClutterPadding *padding)
|
|
{
|
|
if (G_LIKELY (padding))
|
|
{
|
|
g_slice_free (ClutterPadding, padding);
|
|
}
|
|
}
|
|
|
|
static ClutterPadding *
|
|
clutter_padding_copy (const ClutterPadding *padding)
|
|
{
|
|
ClutterPadding *copy;
|
|
|
|
g_return_val_if_fail (padding != NULL, NULL);
|
|
|
|
copy = g_slice_new (ClutterPadding);
|
|
*copy = *padding;
|
|
|
|
return copy;
|
|
}
|
|
|
|
GType
|
|
clutter_padding_get_type (void)
|
|
{
|
|
static GType gtype = 0;
|
|
|
|
if (G_UNLIKELY (gtype == 0))
|
|
gtype = g_boxed_type_register_static (g_intern_static_string ("ClutterPadding"),
|
|
(GBoxedCopyFunc) clutter_padding_copy,
|
|
(GBoxedFreeFunc) clutter_padding_free);
|
|
|
|
return gtype;
|
|
}
|