#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;
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,
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)
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;
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;
/* 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;
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;
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,
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
/**
* 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
/**
* 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);
}
/**
*/
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;
}
#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;
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);
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);