Add ClutterContent

ClutterContent is an interface for creating delegate objects that handle
what an actor is going to paint.

Since they are a newly added type, they only hook into the new PaintNode
based API.

The position and size of the content is controlled in part by the
content's own preferred size, and by the ClutterContentGravity
enumeration.
This commit is contained in:
Emmanuele Bassi 2012-02-01 18:33:55 +00:00
parent 6f0782e0a4
commit be4746b15f
11 changed files with 1091 additions and 3 deletions

View File

@ -74,6 +74,7 @@ source_h = \
$(srcdir)/clutter-colorize-effect.h \
$(srcdir)/clutter-constraint.h \
$(srcdir)/clutter-container.h \
$(srcdir)/clutter-content.h \
$(srcdir)/clutter-deform-effect.h \
$(srcdir)/clutter-deprecated.h \
$(srcdir)/clutter-desaturate-effect.h \
@ -151,6 +152,7 @@ source_c = \
$(srcdir)/clutter-colorize-effect.c \
$(srcdir)/clutter-constraint.c \
$(srcdir)/clutter-container.c \
$(srcdir)/clutter-content.c \
$(srcdir)/clutter-deform-effect.c \
$(srcdir)/clutter-desaturate-effect.c \
$(srcdir)/clutter-device-manager.c \
@ -209,6 +211,7 @@ source_h_priv = \
$(srcdir)/clutter-actor-private.h \
$(srcdir)/clutter-backend-private.h \
$(srcdir)/clutter-bezier.h \
$(srcdir)/clutter-content-private.h \
$(srcdir)/clutter-debug.h \
$(srcdir)/clutter-device-manager-private.h \
$(srcdir)/clutter-easing.h \

View File

@ -380,6 +380,7 @@
#include "clutter-color.h"
#include "clutter-constraint.h"
#include "clutter-container.h"
#include "clutter-content-private.h"
#include "clutter-debug.h"
#include "clutter-effect-private.h"
#include "clutter-enum-types.h"
@ -506,6 +507,11 @@ struct _ClutterActorPrivate
/* delegate object used to allocate the children of this actor */
ClutterLayoutManager *layout_manager;
/* delegate object used to paint the contents of this actor */
ClutterContent *content;
ClutterContentGravity content_gravity;
/* used when painting, to update the paint volume */
ClutterEffect *current_effect;
@ -668,6 +674,10 @@ enum
PROP_FIRST_CHILD,
PROP_LAST_CHILD,
PROP_CONTENT,
PROP_CONTENT_GRAVITY,
PROP_CONTENT_BOX,
PROP_LAST
};
@ -2045,6 +2055,10 @@ clutter_actor_set_allocation_internal (ClutterActor *self,
g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]);
/* if the allocation changes, so does the content box */
if (priv->content != NULL)
g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]);
retval = TRUE;
}
else
@ -3109,6 +3123,9 @@ clutter_actor_paint_node (ClutterActor *actor,
clutter_paint_node_unref (node);
}
if (priv->content != NULL)
_clutter_content_paint_content (priv->content, actor, root);
if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL)
CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root);
@ -4353,6 +4370,14 @@ clutter_actor_set_property (GObject *object,
clutter_actor_set_background_color (actor, g_value_get_boxed (value));
break;
case PROP_CONTENT:
clutter_actor_set_content (actor, g_value_get_object (value));
break;
case PROP_CONTENT_GRAVITY:
clutter_actor_set_content_gravity (actor, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -4751,6 +4776,23 @@ clutter_actor_get_property (GObject *object,
g_value_set_object (value, priv->last_child);
break;
case PROP_CONTENT:
g_value_set_object (value, priv->content);
break;
case PROP_CONTENT_GRAVITY:
g_value_set_enum (value, priv->content_gravity);
break;
case PROP_CONTENT_BOX:
{
ClutterActorBox box = { 0, };
clutter_actor_get_content_box (actor, &box);
g_value_set_boxed (value, &box);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -4807,8 +4849,13 @@ clutter_actor_dispose (GObject *object)
if (priv->layout_manager != NULL)
{
clutter_layout_manager_set_container (priv->layout_manager, NULL);
g_object_unref (priv->layout_manager);
priv->layout_manager = NULL;
g_clear_object (&priv->layout_manager);
}
if (priv->content != NULL)
{
_clutter_content_detached (priv->content, self);
g_clear_object (&priv->content);
}
G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
@ -6152,6 +6199,66 @@ clutter_actor_class_init (ClutterActorClass *klass)
CLUTTER_TYPE_ACTOR,
CLUTTER_PARAM_READABLE);
/**
* ClutterActor:content:
*
* The #ClutterContent implementation that controls the content
* of the actor.
*
* Since: 1.10
*/
obj_props[PROP_CONTENT] =
g_param_spec_object ("content",
P_("Content"),
P_("Delegate object for painting the actor's content"),
CLUTTER_TYPE_CONTENT,
CLUTTER_PARAM_READWRITE);
/**
* ClutterActor:content-gravity:
*
* The alignment that should be honoured by the #ClutterContent
* set with the #ClutterActor:content property.
*
* Changing the value of this property will change the bounding box of
* the content; you can use the #ClutterActor:content-box property to
* get the position and size of the content within the actor's
* allocation.
*
* This property is meaningful only for #ClutterContent implementations
* that have a preferred size, and if the preferred size is smaller than
* the actor's allocation.
*
* Since: 1.10
*/
obj_props[PROP_CONTENT_GRAVITY] =
g_param_spec_enum ("content-gravity",
P_("Content Gravity"),
P_("Alignment of the actor's content"),
CLUTTER_TYPE_CONTENT_GRAVITY,
CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
CLUTTER_PARAM_READWRITE);
/**
* ClutterActor:content-box:
*
* The bounding box for the #ClutterContent used by the actor.
*
* The value of this property is controlled by the #ClutterActor:allocation
* and #ClutterActor:content-gravity properties of #ClutterActor.
*
* The bounding box for the content is guaranteed to never exceed the
* allocation's of the actor.
*
* Since: 1.10
*/
obj_props[PROP_CONTENT_BOX] =
g_param_spec_boxed ("content-box",
P_("Content Box"),
P_("The bounding box of the actor's content"),
CLUTTER_TYPE_ACTOR_BOX,
CLUTTER_PARAM_READABLE);
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
/**
@ -6731,6 +6838,12 @@ clutter_actor_init (ClutterActor *self)
priv->last_paint_volume_valid = TRUE;
priv->transform_valid = FALSE;
/* the default is to stretch the content, to match the
* current behaviour of basically all actors. also, it's
* the easiest thing to compute.
*/
priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL;
}
/**
@ -17236,3 +17349,303 @@ clutter_actor_restore_easing_state (ClutterActor *self)
g_array_remove_index (info->states, info->states->len - 1);
info->cur_state = &g_array_index (info->states, AState, info->states->len - 1);
}
/**
* clutter_actor_set_content:
* @self: a #ClutterActor
* @content: (allow-none): a #ClutterContent, or %NULL
*
* Sets the contents of a #ClutterActor.
*
* Since: 1.10
*/
void
clutter_actor_set_content (ClutterActor *self,
ClutterContent *content)
{
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content));
priv = self->priv;
if (priv->content != NULL)
{
_clutter_content_detached (priv->content, self);
g_object_unref (priv->content);
}
priv->content = content;
if (priv->content != NULL)
{
g_object_ref (priv->content);
_clutter_content_attached (priv->content, self);
}
/* given that the content is always painted within the allocation,
* we only need to queue a redraw here
*/
clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]);
/* if the content gravity is not resize-fill, and the new content has a
* different preferred size than the previous one, then the content box
* may have been changed. since we compute that lazily, we just notify
* here, and let whomever watches :content-box do whatever they need to
* do.
*/
if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
}
/**
* clutter_actor_get_content:
* @self: a #ClutterActor
*
* Retrieves the contents of @self.
*
* Return value: (transfer none): a pointer to the #ClutterContent instance,
* or %NULL if none was set
*
* Since: 1.10
*/
ClutterContent *
clutter_actor_get_content (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
return self->priv->content;
}
/**
* clutter_actor_set_content_gravity:
* @self: a #ClutterActor
* @gravity: the #ClutterContentGravity
*
* Sets the gravity of the #ClutterContent used by @self.
*
* See the description of the #ClutterActor:content-gravity property for
* more information.
*
* Since: 1.10
*/
void
clutter_actor_set_content_gravity (ClutterActor *self,
ClutterContentGravity gravity)
{
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
priv = self->priv;
if (priv->content_gravity == gravity)
return;
priv->content_gravity = gravity;
clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]);
}
/**
* clutter_actor_get_content_gravity:
* @self: a #ClutterActor
*
* Retrieves the content gravity as set using
* clutter_actor_get_content_gravity().
*
* Return value: the content gravity
*
* Since: 1.10
*/
ClutterContentGravity
clutter_actor_get_content_gravity (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self),
CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
return self->priv->content_gravity;
}
/**
* clutter_actor_get_content_box:
* @self: a #ClutterActor
* @box: (out caller-allocates): the return location for the bounding
* box for the #ClutterContent
*
* Retrieves the bounding box for the #ClutterContent of @self.
*
* If no #ClutterContent is set for @self, or if @self has not been
* allocated yet, then the result is undefined.
*
* The content box is guaranteed to be, at most, as big as the allocation
* of the #ClutterActor.
*
* If the #ClutterContent used by the actor has a preferred size, then
* it is possible to modify the content box by using the
* #ClutterActor:content-gravity property.
*
* Since: 1.10
*/
void
clutter_actor_get_content_box (ClutterActor *self,
ClutterActorBox *box)
{
ClutterActorPrivate *priv;
gfloat content_w, content_h;
gfloat alloc_w, alloc_h;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (box != NULL);
priv = self->priv;
if (!clutter_actor_has_allocation (self))
return;
if (priv->content == NULL)
return;
*box = priv->allocation;
/* no need to do any more work */
if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL)
return;
/* if the content does not have a preferred size then there is
* no point in computing the content box
*/
if (!_clutter_content_get_preferred_size (priv->content,
&content_w,
&content_h))
return;
clutter_actor_box_get_size (&priv->allocation, &alloc_w, &alloc_h);
switch (priv->content_gravity)
{
case CLUTTER_CONTENT_GRAVITY_TOP_LEFT:
box->x2 = box->x1 + MIN (content_w, alloc_w);
box->y2 = box->y1 + MIN (content_h, alloc_h);
break;
case CLUTTER_CONTENT_GRAVITY_TOP:
if (alloc_w > content_w)
{
box->x1 += ceilf ((alloc_w - content_w) / 2.0);
box->x2 = box->x1 + content_w;
}
box->y2 = box->y1 + MIN (content_h, alloc_h);
break;
case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT:
if (alloc_w > content_w)
{
box->x1 += (alloc_w - content_w);
box->x2 = box->x1 + content_w;
}
box->y2 = box->y1 + MIN (content_h, alloc_h);
break;
case CLUTTER_CONTENT_GRAVITY_LEFT:
box->x2 = box->x1 + MIN (content_w, alloc_w);
if (alloc_h > content_h)
{
box->y1 += ceilf ((alloc_h - content_h) / 2.0);
box->y2 = box->y1 + content_h;
}
break;
case CLUTTER_CONTENT_GRAVITY_CENTER:
if (alloc_w > content_w)
{
box->x1 += ceilf ((alloc_w - content_w) / 2.0);
box->x2 = box->x1 + content_w;
}
if (alloc_h > content_h)
{
box->y1 += ceilf ((alloc_h - content_h) / 2.0);
box->y2 = box->y1 + content_h;
}
break;
case CLUTTER_CONTENT_GRAVITY_RIGHT:
if (alloc_w > content_w)
{
box->x1 += (alloc_w - content_w);
box->x2 = box->x1 + content_w;
}
if (alloc_h > content_h)
{
box->y1 += ceilf ((alloc_h - content_h) / 2.0);
box->y2 = box->y1 + content_h;
}
break;
case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT:
box->x2 = box->x1 + MIN (content_w, alloc_w);
if (alloc_h > content_h)
{
box->y1 += (alloc_h - content_h);
box->y2 = box->y1 + content_h;
}
break;
case CLUTTER_CONTENT_GRAVITY_BOTTOM:
if (alloc_w > content_w)
{
box->x1 += ceilf ((alloc_w - content_w) / 2.0);
box->x2 = box->x1 + content_w;
}
if (alloc_h > content_h)
{
box->y1 += (alloc_h - content_h);
box->y2 = box->y1 + content_h;
}
break;
case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT:
if (alloc_w > content_w)
{
box->x1 += (alloc_w - content_w);
box->x2 = box->x1 + content_w;
}
if (alloc_h > content_h)
{
box->y1 += (alloc_h - content_h);
box->y2 = box->y1 + content_h;
}
break;
case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL:
g_assert_not_reached ();
break;
case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT:
if (content_w >= content_h && content_h > 0)
{
double ratio = content_w / content_h;
box->x2 = box->x1 + alloc_w;
box->y1 += ceilf ((alloc_h - (alloc_h / ratio)) / 2.0);
box->y2 = box->y1 + (alloc_h / ratio);
}
else if (content_h > content_w && content_w > 0)
{
double ratio = content_h / content_w;
box->x1 += ceilf ((alloc_w - (alloc_w / ratio)) / 2.0);
box->x2 = box->x2 + (alloc_w / ratio);
box->y2 = box->x1 + alloc_h;
}
break;
}
}

View File

@ -467,6 +467,21 @@ gboolean clutter_actor_is_in_clone_paint
gboolean clutter_actor_get_paint_box (ClutterActor *self,
ClutterActorBox *box);
gboolean clutter_actor_has_overlaps (ClutterActor *self);
/* Content */
CLUTTER_AVAILABLE_IN_1_10
void clutter_actor_set_content (ClutterActor *self,
ClutterContent *content);
CLUTTER_AVAILABLE_IN_1_10
ClutterContent * clutter_actor_get_content (ClutterActor *self);
CLUTTER_AVAILABLE_IN_1_10
void clutter_actor_set_content_gravity (ClutterActor *self,
ClutterContentGravity gravity);
CLUTTER_AVAILABLE_IN_1_10
ClutterContentGravity clutter_actor_get_content_gravity (ClutterActor *self);
CLUTTER_AVAILABLE_IN_1_10
void clutter_actor_get_content_box (ClutterActor *self,
ClutterActorBox *box);
CLUTTER_AVAILABLE_IN_1_10
void clutter_actor_set_background_color (ClutterActor *self,
const ClutterColor *color);

View File

@ -0,0 +1,47 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2011 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:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifndef __CLUTTER_CONTENT_PRIVATE_H__
#define __CLUTTER_CONTENT_PRIVATE_H__
#include <clutter/clutter-content.h>
G_BEGIN_DECLS
gboolean _clutter_content_get_preferred_size (ClutterContent *content,
gfloat *width,
gfloat *height);
void _clutter_content_attached (ClutterContent *content,
ClutterActor *actor);
void _clutter_content_detached (ClutterContent *content,
ClutterActor *actor);
void _clutter_content_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *node);
G_END_DECLS
#endif /* __CLUTTER_CONTENT_PRIVATE_H__ */

248
clutter/clutter-content.c Normal file
View File

@ -0,0 +1,248 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2011 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:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* SECTION:clutter-content
* @Title: ClutterContent
* @short_desc: Delegate for painting the content of an actor
*
* #ClutterContent is an interface to implement types responsible for
* painting the content of a #ClutterActor.
*
* Multiple actors can use the same #ClutterContent instance, in order
* to share the resources associated with painting the same content.
*
* #ClutterContent is available since Clutter 1.10.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-content-private.h"
#include "clutter-debug.h"
#include "clutter-marshal.h"
#include "clutter-private.h"
typedef struct _ClutterContentIface ClutterContentInterface;
static GQuark quark_content_actors = 0;
G_DEFINE_INTERFACE (ClutterContent, clutter_content, G_TYPE_OBJECT)
static gboolean
clutter_content_real_get_preferred_size (ClutterContent *content,
gfloat *width,
gfloat *height)
{
if (width != NULL)
*width = 0.f;
if (height != NULL)
*height = 0.f;
return FALSE;
}
static void
clutter_content_real_attached (ClutterContent *content,
ClutterActor *actor)
{
GObject *obj = G_OBJECT (content);
GHashTable *actors;
actors = g_object_get_qdata (obj, quark_content_actors);
if (actors == NULL)
{
actors = g_hash_table_new (NULL, NULL);
g_object_set_qdata_full (obj, quark_content_actors,
actors,
(GDestroyNotify) g_hash_table_unref);
}
g_hash_table_insert (actors, actor, actor);
}
static void
clutter_content_real_detached (ClutterContent *content,
ClutterActor *actor)
{
GObject *obj = G_OBJECT (content);
GHashTable *actors;
actors = g_object_get_qdata (obj, quark_content_actors);
g_assert (actors != NULL);
g_hash_table_remove (actors, actor);
if (g_hash_table_size (actors) == 0)
g_object_set_qdata (obj, quark_content_actors, NULL);
}
static void
clutter_content_real_invalidate (ClutterContent *content)
{
GHashTable *actors;
GHashTableIter iter;
gpointer key_p, value_p;
actors = g_object_get_qdata (G_OBJECT (content), quark_content_actors);
if (actors == NULL)
return;
g_hash_table_iter_init (&iter, actors);
while (g_hash_table_iter_next (&iter, &key_p, &value_p))
{
ClutterActor *actor = key_p;
g_assert (actor != NULL);
clutter_actor_queue_redraw (actor);
}
}
static void
clutter_content_real_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *context)
{
}
static void
clutter_content_default_init (ClutterContentInterface *iface)
{
quark_content_actors = g_quark_from_static_string ("-clutter-content-actors");
iface->get_preferred_size = clutter_content_real_get_preferred_size;
iface->paint_content = clutter_content_real_paint_content;
iface->attached = clutter_content_real_attached;
iface->detached = clutter_content_real_detached;
iface->invalidate = clutter_content_real_invalidate;
}
/**
* clutter_content_invalidate:
* @content: a #ClutterContent
*
* Invalidates a #ClutterContent.
*
* This function should be called by #ClutterContent implementations when
* they change the way a the content should be painted regardless of the
* actor state.
*
* Since: 1.10
*/
void
clutter_content_invalidate (ClutterContent *content)
{
g_return_if_fail (CLUTTER_IS_CONTENT (content));
CLUTTER_CONTENT_GET_IFACE (content)->invalidate (content);
}
/*< private >
* _clutter_content_attached:
* @content: a #ClutterContent
* @actor: a #ClutterActor
*
* Attaches @actor to the @content.
*
* This function should be used internally every time a #ClutterActor
* is associated to a #ClutterContent, to set up a backpointer from
* the @content to the @actor.
*
* This function will invoke the #ClutterContentIface.attached() virtual
* function.
*/
void
_clutter_content_attached (ClutterContent *content,
ClutterActor *actor)
{
CLUTTER_CONTENT_GET_IFACE (content)->attached (content, actor);
}
/*< private >
* _clutter_content_detached:
* @content: a #ClutterContent
* @actor: a #ClutterActor
*
* Detaches @actor from @content.
*
* This function should be used internally every time a #ClutterActor
* removes the association with a #ClutterContent.
*
* This function will invoke the #ClutterContentIface.detached() virtual
* function.
*/
void
_clutter_content_detached (ClutterContent *content,
ClutterActor *actor)
{
CLUTTER_CONTENT_GET_IFACE (content)->detached (content, actor);
}
/*< private >
* _clutter_content_paint_content:
* @content: a #ClutterContent
* @actor: a #ClutterActor
* @context: a #ClutterPaintNode
*
* Creates the render tree for the @content and @actor.
*
* This function will invoke the #ClutterContentIface.paint_content()
* virtual function.
*/
void
_clutter_content_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *node)
{
CLUTTER_CONTENT_GET_IFACE (content)->paint_content (content, actor, node);
}
/*< private >
* _clutter_content_get_preferred_size:
* @content: a #ClutterContent
* @width: (out): return location for the natural width of the content
* @height: (out): return location for the natural height of the content
*
* Retrieves the natural size of the @content, if any.
*
* The natural size of a #ClutterContent is defined as the size the content
* would have regardless of the allocation of the actor that is painting it,
* for instance the size of an image data.
*
* Return value: %TRUE if the content has a preferred size, and %FALSE
* otherwise
*/
gboolean
_clutter_content_get_preferred_size (ClutterContent *content,
gfloat *width,
gfloat *height)
{
return CLUTTER_CONTENT_GET_IFACE (content)->get_preferred_size (content,
width,
height);
}

100
clutter/clutter-content.h Normal file
View File

@ -0,0 +1,100 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2011 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:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_CONTENT_H__
#define __CLUTTER_CONTENT_H__
#include <clutter/clutter-types.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_CONTENT (clutter_content_get_type ())
#define CLUTTER_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CONTENT, ClutterContent))
#define CLUTTER_IS_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CONTENT))
#define CLUTTER_CONTENT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_CONTENT, ClutterContentIface))
typedef struct _ClutterContentIface ClutterContentIface;
/**
* ClutterContent:
*
* The <structname>ClutterContent</structname> structure is an opaque type
* whose members cannot be acccessed directly.
*
* Since: 1.10
*/
/**
* ClutterContentIface:
* @get_preferred_size: virtual function; should be overridden by subclasses
* of #ClutterContent that have a natural size
* @paint_content: virtual function; called each time the content needs to
* paint itself
* @attached: virtual function; called each time a #ClutterContent is attached
* to a #ClutterActor. If overridden, the subclass must chain up to the
* parent class implementation.
* @detached: virtual function; called each time a #ClutterContent is detached
* from a #ClutterActor. If overridden, the subclass must chain up to the
* parent class implementation.
* @invalidate: virtual function; called each time a #ClutterContent state
* is changed. If overridden, the subclass must chain up to the parent
* class implementation.
*
* The <structname>ClutterContentIface</structname> structure contains only
* private data.
*
* Since: 1.10
*/
struct _ClutterContentIface
{
/*< private >*/
GTypeInterface g_iface;
/*< public >*/
gboolean (* get_preferred_size) (ClutterContent *content,
gfloat *width,
gfloat *height);
void (* paint_content) (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *node);
void (* attached) (ClutterContent *content,
ClutterActor *actor);
void (* detached) (ClutterContent *content,
ClutterActor *actor);
void (* invalidate) (ClutterContent *content);
};
GType clutter_content_get_type (void) G_GNUC_CONST;
void clutter_content_invalidate (ClutterContent *content);
G_END_DECLS
#endif /* __CLUTTER_CONTENT_H__ */

View File

@ -1113,6 +1113,42 @@ typedef enum {
CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD = 1 << 2
} ClutterRepaintFlags;
/**
* ClutterContentGravity:
* @CLUTTER_CONTENT_GRAVITY_TOP_LEFT: Align the content to the top left corner
* @CLUTTER_CONTENT_GRAVITY_TOP: Align the content to the top edge
* @CLUTTER_CONTENT_GRAVITY_TOP_RIGHT: Align the content to the top right corner
* @CLUTTER_CONTENT_GRAVITY_LEFT: Align the content to the left edge
* @CLUTTER_CONTENT_GRAVITY_CENTER: Align the content to the center
* @CLUTTER_CONTENT_GRAVITY_RIGHT: Align the content to the right edge
* @CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT: Align the content to the bottom left corner
* @CLUTTER_CONTENT_GRAVITY_BOTTOM: Align the content to the bottom edge
* @CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT: Align the content to the bottom right corner
* @CLUTTER_CONTENT_GRAVITY_RESIZE_FILL: Resize the content to fill the allocation
* @CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT: Resize the content to remain within the
* allocation, while maintaining the aspect ratio
*
* Controls the alignment of the #ClutterContent inside a #ClutterActor.
*
* Since: 1.10
*/
typedef enum {
CLUTTER_CONTENT_GRAVITY_TOP_LEFT,
CLUTTER_CONTENT_GRAVITY_TOP,
CLUTTER_CONTENT_GRAVITY_TOP_RIGHT,
CLUTTER_CONTENT_GRAVITY_LEFT,
CLUTTER_CONTENT_GRAVITY_CENTER,
CLUTTER_CONTENT_GRAVITY_RIGHT,
CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT,
CLUTTER_CONTENT_GRAVITY_BOTTOM,
CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT,
CLUTTER_CONTENT_GRAVITY_RESIZE_FILL,
CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT
} ClutterContentGravity;
G_END_DECLS
#endif /* __CLUTTER_ENUMS_H__ */

View File

@ -54,6 +54,7 @@ typedef struct _ClutterActorMeta ClutterActorMeta;
typedef struct _ClutterLayoutManager ClutterLayoutManager;
typedef struct _ClutterActorIter ClutterActorIter;
typedef struct _ClutterPaintNode ClutterPaintNode;
typedef struct _ClutterContent ClutterContent; /* dummy */
typedef struct _ClutterAlpha ClutterAlpha;
typedef struct _ClutterAnimatable ClutterAnimatable; /* dummy */

View File

@ -55,6 +55,7 @@
#include "clutter-colorize-effect.h"
#include "clutter-constraint.h"
#include "clutter-container.h"
#include "clutter-content.h"
#include "clutter-deform-effect.h"
#include "clutter-desaturate-effect.h"
#include "clutter-device-manager.h"

View File

@ -56,7 +56,8 @@ UNIT_TESTS = \
test-drop.c \
test-devices.c \
test-actor.c \
test-transitions.c
test-transitions.c \
test-content.c
if X11_TESTS
UNIT_TESTS += test-pixmap.c

View File

@ -0,0 +1,223 @@
#include <stdlib.h>
#include <gmodule.h>
#include <clutter/clutter.h>
typedef struct _ColorContent {
GObject parent_instance;
double red;
double green;
double blue;
double alpha;
float padding;
} ColorContent;
typedef struct _ColorContentClass {
GObjectClass parent_class;
} ColorContentClass;
static void clutter_content_iface_init (ClutterContentIface *iface);
G_DEFINE_TYPE_WITH_CODE (ColorContent, color_content, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
clutter_content_iface_init))
static void
color_content_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *root)
{
ColorContent *self = (ColorContent *) content;
ClutterActorBox box, content_box;
ClutterColor color;
PangoLayout *layout;
PangoRectangle logical;
ClutterPaintNode *node;
#if 0
g_debug ("Painting content [%p] "
"{ r:%.2f, g:%.2f, b:%.2f, a:%.2f } "
"for actor [%p] (context: [%p])",
content,
self->red,
self->green,
self->blue,
self->alpha,
actor, context);
#endif
clutter_actor_get_content_box (actor, &content_box);
box = content_box;
box.x1 += self->padding;
box.y1 += self->padding;
box.x2 -= self->padding;
box.y2 -= self->padding;
color.alpha = self->alpha * 255;
color.red = self->red * 255;
color.green = self->green * 255;
color.blue = self->blue * 255;
node = clutter_color_node_new (&color);
clutter_paint_node_add_rectangle (node, &box);
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
color.red = (1.0 - self->red) * 255;
color.green = (1.0 - self->green) * 255;
color.blue = (1.0 - self->blue) * 255;
layout = clutter_actor_create_pango_layout (actor, "A");
pango_layout_get_pixel_extents (layout, NULL, &logical);
node = clutter_text_node_new (layout, &color);
/* top-left */
box.x1 = clutter_actor_box_get_x (&content_box);
box.y1 = clutter_actor_box_get_y (&content_box);
clutter_paint_node_add_rectangle (node, &box);
/* top-right */
box.x1 = clutter_actor_box_get_x (&content_box)
+ clutter_actor_box_get_width (&content_box)
- logical.width;
box.y1 = clutter_actor_box_get_y (&content_box);
clutter_paint_node_add_rectangle (node, &box);
/* bottom-right */
box.x1 = clutter_actor_box_get_x (&content_box)
+ clutter_actor_box_get_width (&content_box)
- logical.width;
box.y1 = clutter_actor_box_get_y (&content_box)
+ clutter_actor_box_get_height (&content_box)
- logical.height;
clutter_paint_node_add_rectangle (node, &box);
/* bottom-left */
box.x1 = clutter_actor_box_get_x (&content_box);
box.y1 = clutter_actor_box_get_y (&content_box)
+ clutter_actor_box_get_height (&content_box)
- logical.height;
clutter_paint_node_add_rectangle (node, &box);
/* center */
box.x1 = clutter_actor_box_get_x (&content_box)
+ (clutter_actor_box_get_width (&content_box) - logical.width) / 2.0;
box.y1 = clutter_actor_box_get_y (&content_box)
+ (clutter_actor_box_get_height (&content_box) - logical.height) / 2.0;
clutter_paint_node_add_rectangle (node, &box);
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
g_object_unref (layout);
}
static void
clutter_content_iface_init (ClutterContentIface *iface)
{
iface->paint_content = color_content_paint_content;
}
static void
color_content_class_init (ColorContentClass *klass)
{
}
static void
color_content_init (ColorContent *self)
{
}
static ClutterContent *
color_content_new (double red,
double green,
double blue,
double alpha,
float padding)
{
ColorContent *self = g_object_new (color_content_get_type (), NULL);
self->red = red;
self->green = green;
self->blue = blue;
self->alpha = alpha;
self->padding = padding;
return (ClutterContent *) self;
}
G_MODULE_EXPORT int
test_content_main (int argc, char *argv[])
{
ClutterActor *stage, *grid;
ClutterContent *content;
int i, n_rects;
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return EXIT_FAILURE;
stage = clutter_stage_new ();
clutter_actor_set_name (stage, "Stage");
clutter_stage_set_title (CLUTTER_STAGE (stage), "Content");
clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
clutter_actor_show (stage);
grid = clutter_actor_new ();
clutter_actor_set_name (grid, "Grid");
clutter_actor_set_margin_top (grid, 12);
clutter_actor_set_margin_right (grid, 12);
clutter_actor_set_margin_bottom (grid, 12);
clutter_actor_set_margin_left (grid, 12);
clutter_actor_set_layout_manager (grid, clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL));
clutter_actor_add_constraint (grid, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0.0));
clutter_actor_add_child (stage, grid);
content = color_content_new (g_random_double_range (0.0, 1.0),
g_random_double_range (0.0, 1.0),
g_random_double_range (0.0, 1.0),
1.0,
2.0);
n_rects = g_random_int_range (12, 24);
for (i = 0; i < n_rects; i++)
{
ClutterActor *box = clutter_actor_new ();
ClutterColor bg_color = {
g_random_int_range (0, 255),
g_random_int_range (0, 255),
g_random_int_range (0, 255),
255
};
char *name, *color;
color = clutter_color_to_string (&bg_color);
name = g_strconcat ("Box <", color, ">", NULL);
clutter_actor_set_name (box, name);
g_free (name);
g_free (color);
clutter_actor_set_background_color (box, &bg_color);
clutter_actor_set_content (box, content);
clutter_actor_set_size (box, 64, 64);
clutter_actor_add_child (grid, box);
}
clutter_main ();
g_object_unref (content);
return EXIT_SUCCESS;
}
G_MODULE_EXPORT const char *
test_content_describe (void)
{
return "A simple test for ClutterContent";
}