[layout] Add layers to BinLayout

Each actor managed by a BinLayout policy should reside inside its
own "layer", with horizontal and vertical alignment. The :x-align
and :y-align properties of the BinLayout are the default alignment
policies, which are copied to each new "layer" when it is created.

The set_alignment() and get_alignment() methods of BinLayout can
be changed to operate on a specific "layer".

The whole machinery uses the new ChildMeta support inside the
LayoutManager base abstract class.
This commit is contained in:
Emmanuele Bassi 2009-09-14 21:51:49 +01:00
parent 98474076a1
commit 9cccff504a
3 changed files with 385 additions and 31 deletions

View File

@ -40,12 +40,20 @@
#include "clutter-actor.h"
#include "clutter-animatable.h"
#include "clutter-bin-layout.h"
#include "clutter-child-meta.h"
#include "clutter-debug.h"
#include "clutter-enum-types.h"
#include "clutter-private.h"
#define CLUTTER_TYPE_BIN_LAYER (clutter_bin_layer_get_type ())
#define CLUTTER_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYER, ClutterBinLayer))
#define CLUTTER_IS_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYER))
#define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate))
typedef struct _ClutterBinLayer ClutterBinLayer;
typedef struct _ClutterChildMetaClass ClutterBinLayerClass;
struct _ClutterBinLayoutPrivate
{
ClutterBinAlignment x_align;
@ -55,6 +63,24 @@ struct _ClutterBinLayoutPrivate
gdouble y_factor;
};
struct _ClutterBinLayer
{
ClutterChildMeta parent_instance;
ClutterBinLayout *layout;
ClutterBinAlignment x_align;
ClutterBinAlignment y_align;
};
enum
{
PROP_LAYER_0,
PROP_LAYER_X_ALIGN,
PROP_LAYER_Y_ALIGN
};
enum
{
PROP_0,
@ -63,11 +89,146 @@ enum
PROP_Y_ALIGN
};
G_DEFINE_TYPE (ClutterBinLayer,
clutter_bin_layer,
CLUTTER_TYPE_CHILD_META);
G_DEFINE_TYPE (ClutterBinLayout,
clutter_bin_layout,
CLUTTER_TYPE_LAYOUT_MANAGER);
/*
* ClutterBinLayer
*/
static void
set_layer_x_align (ClutterBinLayer *self,
ClutterBinAlignment alignment)
{
ClutterLayoutManager *manager;
if (self->x_align == alignment)
return;
self->x_align = alignment;
g_assert (self->layout != NULL);
manager = CLUTTER_LAYOUT_MANAGER (self->layout);
clutter_layout_manager_layout_changed (manager);
g_object_notify (G_OBJECT (self), "x-align");
}
static void
set_layer_y_align (ClutterBinLayer *self,
ClutterBinAlignment alignment)
{
ClutterLayoutManager *manager;
if (self->y_align == alignment)
return;
self->y_align = alignment;
g_assert (self->layout != NULL);
manager = CLUTTER_LAYOUT_MANAGER (self->layout);
clutter_layout_manager_layout_changed (manager);
g_object_notify (G_OBJECT (self), "y-align");
}
static void
clutter_bin_layer_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject);
switch (prop_id)
{
case PROP_LAYER_X_ALIGN:
set_layer_x_align (layer, g_value_get_enum (value));
break;
case PROP_LAYER_Y_ALIGN:
set_layer_y_align (layer, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_bin_layer_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject);
switch (prop_id)
{
case PROP_LAYER_X_ALIGN:
g_value_set_enum (value, layer->x_align);
break;
case PROP_LAYER_Y_ALIGN:
g_value_set_enum (value, layer->y_align);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_bin_layer_class_init (ClutterBinLayerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = clutter_bin_layer_set_property;
gobject_class->get_property = clutter_bin_layer_get_property;
pspec = g_param_spec_enum ("x-align",
"X Align",
"Horizontal alignment for the actor "
"inside the layer",
CLUTTER_TYPE_BIN_ALIGNMENT,
CLUTTER_BIN_ALIGNMENT_CENTER,
CLUTTER_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_LAYER_X_ALIGN,
pspec);
pspec = g_param_spec_enum ("y-align",
"Y Align",
"Vertical alignment for the actor "
"inside the layer manager",
CLUTTER_TYPE_BIN_ALIGNMENT,
CLUTTER_BIN_ALIGNMENT_CENTER,
CLUTTER_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_LAYER_Y_ALIGN,
pspec);
}
static void
clutter_bin_layer_init (ClutterBinLayer *layer)
{
layer->x_align = CLUTTER_BIN_ALIGNMENT_CENTER;
layer->y_align = CLUTTER_BIN_ALIGNMENT_CENTER;
}
/*
* ClutterBinLayout
*/
static void
set_x_align (ClutterBinLayout *self,
ClutterBinAlignment alignment)
@ -200,7 +361,6 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager,
const ClutterActorBox *allocation,
ClutterAllocationFlags flags)
{
ClutterBinLayoutPrivate *priv = CLUTTER_BIN_LAYOUT (manager)->priv;
GList *children = clutter_container_get_children (container);
GList *l;
gfloat available_w, available_h;
@ -211,17 +371,24 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager,
for (l = children; l != NULL; l = l->next)
{
ClutterActor *child = l->data;
ClutterChildMeta *meta;
ClutterBinLayer *layer;
ClutterActorBox child_alloc = { 0, };
gfloat child_width, child_height;
ClutterRequestMode request;
if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL)
meta = clutter_layout_manager_get_child_meta (manager,
container,
child);
layer = CLUTTER_BIN_LAYER (meta);
if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL)
{
child_alloc.x1 = (int) 0;
child_alloc.x2 = (int) available_w;
}
if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
{
child_alloc.y1 = (int) 0;
child_alloc.y2 = (int) available_h;
@ -230,11 +397,11 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager,
/* if we are filling horizontally and vertically then we
* can break here because we already have a full allocation
*/
if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FILL &&
priv->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL &&
layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
{
clutter_actor_allocate (child, &child_alloc, flags);
break;
continue;
}
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
@ -270,32 +437,32 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager,
child_width = CLAMP (nat_width, min_width, available_w);
}
if (priv->x_align == CLUTTER_BIN_ALIGNMENT_FIXED)
if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED)
{
child_alloc.x1 = (int) clutter_actor_get_x (child);
child_alloc.x2 = (int) child_alloc.x1 + child_width;
}
else
{
gdouble x_align = get_bin_alignment_factor (priv->x_align);
gdouble x_align = get_bin_alignment_factor (layer->x_align);
if (priv->x_align != CLUTTER_BIN_ALIGNMENT_FILL)
if (layer->x_align != CLUTTER_BIN_ALIGNMENT_FILL)
{
child_alloc.x1 = (int) ((available_w - child_width) * x_align);
child_alloc.x2 = (int) child_alloc.x1 + child_width;
}
}
if (priv->y_align == CLUTTER_BIN_ALIGNMENT_FIXED)
if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED)
{
child_alloc.y1 = (int) clutter_actor_get_y (child);
child_alloc.y2 = (int) child_alloc.y1 + child_height;
}
else
{
gdouble y_align = get_bin_alignment_factor (priv->y_align);
gdouble y_align = get_bin_alignment_factor (layer->y_align);
if (priv->y_align != CLUTTER_BIN_ALIGNMENT_FILL)
if (layer->y_align != CLUTTER_BIN_ALIGNMENT_FILL)
{
child_alloc.y1 = (int) ((available_h - child_height) * y_align);
child_alloc.y2 = (int) child_alloc.y1 + child_height;
@ -308,6 +475,28 @@ clutter_bin_layout_allocate (ClutterLayoutManager *manager,
g_list_free (children);
}
static ClutterChildMeta *
clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager,
ClutterContainer *container,
ClutterActor *actor)
{
ClutterBinLayoutPrivate *priv;
ClutterChildMeta *meta;
priv = CLUTTER_BIN_LAYOUT (manager)->priv;
meta = g_object_new (CLUTTER_TYPE_BIN_LAYER,
"container", container,
"actor", actor,
"x-align", priv->x_align,
"y_align", priv->y_align,
NULL);
CLUTTER_BIN_LAYER (meta)->layout = CLUTTER_BIN_LAYOUT (manager);
return meta;
}
static void
clutter_bin_layout_set_property (GObject *gobject,
guint prop_id,
@ -411,6 +600,8 @@ clutter_bin_layout_class_init (ClutterBinLayoutClass *klass)
clutter_bin_layout_get_preferred_height;
layout_class->allocate =
clutter_bin_layout_allocate;
layout_class->create_child_meta =
clutter_bin_layout_create_child_meta;
}
static void
@ -424,9 +615,9 @@ clutter_bin_layout_init (ClutterBinLayout *self)
/**
* clutter_bin_layout_new:
* @x_align: the #ClutterBinAlignment policy to be used on the
* @x_align: the default alignment policy to be used on the
* horizontal axis
* @y_align: the #ClutterBinAlignment policy to be used on the
* @y_align: the default alignment policy to be used on the
* vertical axis
*
* Creates a new #ClutterBinLayout layout manager
@ -448,29 +639,39 @@ clutter_bin_layout_new (ClutterBinAlignment x_align,
/**
* clutter_bin_layout_set_alignment:
* @self: a #ClutterBinLayout
* @x_align: the #ClutterBinAlignment policy to be used on the
* horizontal axis
* @y_align: the #ClutterBinAlignment policy to be used on the
* vertical axis
* @container: a #ClutterContainer with a layout managed by @self
* @child: a #ClutterActor child of @container
* @x_align: the horizontal alignment policy to be used for the @child
* inside @container
* @y_align: the vertical aligment policy to be used on the @child
* inside @container
*
* Sets the alignment policies on the horizontal and vertical
* axis for @self
* Sets the horizontal and vertical alignment policies to be applied
* to the @child of @container
*
* Since: 1.2
*/
void
clutter_bin_layout_set_alignment (ClutterBinLayout *self,
ClutterContainer *container,
ClutterActor *child,
ClutterBinAlignment x_align,
ClutterBinAlignment y_align)
{
ClutterLayoutManager *manager;
ClutterChildMeta *meta;
ClutterBinLayer *layer;
g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
g_object_freeze_notify (G_OBJECT (self));
manager = CLUTTER_LAYOUT_MANAGER (self);
meta = clutter_layout_manager_get_child_meta (manager, container, child);
g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta));
set_x_align (self, x_align);
set_y_align (self, y_align);
layer = CLUTTER_BIN_LAYER (meta);
g_object_thaw_notify (G_OBJECT (self));
set_layer_x_align (layer, x_align);
set_layer_y_align (layer, y_align);
}
/**
@ -487,14 +688,26 @@ clutter_bin_layout_set_alignment (ClutterBinLayout *self,
*/
void
clutter_bin_layout_get_alignment (ClutterBinLayout *self,
ClutterContainer *container,
ClutterActor *child,
ClutterBinAlignment *x_align,
ClutterBinAlignment *y_align)
{
ClutterLayoutManager *manager;
ClutterChildMeta *meta;
ClutterBinLayer *layer;
g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
manager = CLUTTER_LAYOUT_MANAGER (self);
meta = clutter_layout_manager_get_child_meta (manager, container, child);
g_return_if_fail (CLUTTER_IS_BIN_LAYER (meta));
layer = CLUTTER_BIN_LAYER (meta);
if (x_align)
*x_align = self->priv->x_align;
*x_align = layer->x_align;
if (y_align)
*y_align = self->priv->y_align;
*y_align = layer->y_align;
}

View File

@ -81,9 +81,13 @@ ClutterLayoutManager *clutter_bin_layout_new (ClutterBinAlignment ali
ClutterBinAlignment align_y);
void clutter_bin_layout_set_alignment (ClutterBinLayout *self,
ClutterContainer *container,
ClutterActor *child,
ClutterBinAlignment x_align,
ClutterBinAlignment y_align);
void clutter_bin_layout_get_alignment (ClutterBinLayout *self,
ClutterContainer *container,
ClutterActor *child,
ClutterBinAlignment *x_align,
ClutterBinAlignment *y_align);

View File

@ -1,12 +1,101 @@
#include <stdlib.h>
#include <gmodule.h>
#include <cairo/cairo.h>
#include <clutter/clutter.h>
static ClutterActor *
make_background (const ClutterColor *color,
gfloat width,
gfloat height)
{
ClutterActor *tex = clutter_cairo_texture_new (width, height);
cairo_t *cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (tex));
cairo_pattern_t *pat;
gfloat x, y;
#define BG_ROUND_RADIUS 12
x = y = 0;
cairo_move_to (cr, BG_ROUND_RADIUS, y);
cairo_line_to (cr, width - BG_ROUND_RADIUS, y);
cairo_curve_to (cr, width, y, width, y, width, BG_ROUND_RADIUS);
cairo_line_to (cr, width, height - BG_ROUND_RADIUS);
cairo_curve_to (cr, width, height, width, height, width - BG_ROUND_RADIUS, height);
cairo_line_to (cr, BG_ROUND_RADIUS, height);
cairo_curve_to (cr, x, height, x, height, x, height - BG_ROUND_RADIUS);
cairo_line_to (cr, x, BG_ROUND_RADIUS);
cairo_curve_to (cr, x, y, x, y, BG_ROUND_RADIUS, y);
cairo_close_path (cr);
clutter_cairo_set_source_color (cr, color);
cairo_stroke (cr);
x += 4;
y += 4;
width -= 4;
height -= 4;
cairo_move_to (cr, BG_ROUND_RADIUS, y);
cairo_line_to (cr, width - BG_ROUND_RADIUS, y);
cairo_curve_to (cr, width, y, width, y, width, BG_ROUND_RADIUS);
cairo_line_to (cr, width, height - BG_ROUND_RADIUS);
cairo_curve_to (cr, width, height, width, height, width - BG_ROUND_RADIUS, height);
cairo_line_to (cr, BG_ROUND_RADIUS, height);
cairo_curve_to (cr, x, height, x, height, x, height - BG_ROUND_RADIUS);
cairo_line_to (cr, x, BG_ROUND_RADIUS);
cairo_curve_to (cr, x, y, x, y, BG_ROUND_RADIUS, y);
cairo_close_path (cr);
pat = cairo_pattern_create_linear (0, 0, 0, height);
cairo_pattern_add_color_stop_rgba (pat, 1, .85, .85, .85, 1);
cairo_pattern_add_color_stop_rgba (pat, .95, 1, 1, 1, 1);
cairo_pattern_add_color_stop_rgba (pat, .05, 1, 1, 1, 1);
cairo_pattern_add_color_stop_rgba (pat, 0, .85, .85, .85, 1);
cairo_set_source (cr, pat);
cairo_fill (cr);
cairo_pattern_destroy (pat);
cairo_destroy (cr);
#undef BG_ROUND_RADIUS
return tex;
}
static gboolean
on_box_enter (ClutterActor *box,
ClutterEvent *event,
ClutterActor *emblem)
{
clutter_actor_animate (emblem, CLUTTER_LINEAR, 150,
"opacity", 255,
NULL);
return TRUE;
}
static gboolean
on_box_leave (ClutterActor *box,
ClutterEvent *event,
ClutterActor *emblem)
{
clutter_actor_animate (emblem, CLUTTER_LINEAR, 150,
"opacity", 0,
NULL);
return TRUE;
}
G_MODULE_EXPORT int
test_box_main (int argc, char *argv[])
{
ClutterActor *stage, *box, *rect;
ClutterLayoutManager *layout;
ClutterColor stage_color = { 0xe0, 0xf2, 0xfc, 0xff };
ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x99 };
ClutterColor *color;
@ -14,7 +103,8 @@ test_box_main (int argc, char *argv[])
stage = clutter_stage_get_default ();
clutter_stage_set_title (CLUTTER_STAGE (stage), "Box test");
clutter_actor_set_size (stage, 320, 200);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
clutter_actor_set_size (stage, 640, 480);
layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
CLUTTER_BIN_ALIGNMENT_CENTER);
@ -22,20 +112,67 @@ test_box_main (int argc, char *argv[])
box = clutter_box_new (layout);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
clutter_actor_set_anchor_point_from_gravity (box, CLUTTER_GRAVITY_CENTER);
clutter_actor_set_position (box, 160, 100);
clutter_actor_set_position (box, 320, 240);
clutter_actor_set_reactive (box, TRUE);
clutter_actor_set_name (box, "box");
rect = clutter_rectangle_new_with_color (&bg_color);
rect = make_background (&bg_color, 200, 200);
clutter_container_add_actor (CLUTTER_CONTAINER (box), rect);
clutter_actor_set_size (rect, 100, 100);
clutter_actor_lower_bottom (rect);
clutter_actor_set_name (rect, "background");
clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout),
CLUTTER_CONTAINER (box),
rect,
CLUTTER_BIN_ALIGNMENT_FILL,
CLUTTER_BIN_ALIGNMENT_FILL);
{
ClutterActor *tex;
GError *error;
error = NULL;
tex = clutter_texture_new_from_file ("redhand.png", &error);
if (error)
g_error ("Unable to create texture: %s", error->message);
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (tex), TRUE);
clutter_container_add_actor (CLUTTER_CONTAINER (box), tex);
clutter_actor_raise (tex, rect);
clutter_actor_set_width (tex, 175);
clutter_actor_set_name (tex, "texture");
clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout),
CLUTTER_CONTAINER (box),
tex,
CLUTTER_BIN_ALIGNMENT_CENTER,
CLUTTER_BIN_ALIGNMENT_CENTER);
}
color = clutter_color_new (g_random_int_range (0, 255),
g_random_int_range (0, 255),
g_random_int_range (0, 255),
255);
224);
rect = clutter_rectangle_new_with_color (color);
clutter_container_add_actor (CLUTTER_CONTAINER (box), rect);
clutter_actor_set_size (rect, 50, 50);
clutter_actor_set_opacity (rect, 0);
clutter_actor_raise_top (rect);
clutter_actor_set_name (rect, "emblem");
clutter_bin_layout_set_alignment (CLUTTER_BIN_LAYOUT (layout),
CLUTTER_CONTAINER (box),
rect,
CLUTTER_BIN_ALIGNMENT_END,
CLUTTER_BIN_ALIGNMENT_END);
g_signal_connect (box,
"enter-event", G_CALLBACK (on_box_enter),
rect);
g_signal_connect (box,
"leave-event", G_CALLBACK (on_box_leave),
rect);
clutter_actor_show_all (stage);