From 8a5967f0cc4ca2ce2b4e8869f4a0ec3bdee07912 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 21 Jan 2011 16:43:03 +0000 Subject: [PATCH 01/18] docs: Add custom ClutterActor example which uses composition --- doc/cookbook/examples/Makefile.am | 2 + doc/cookbook/examples/actors-composite-main.c | 79 +++++ doc/cookbook/examples/cb-button.c | 287 ++++++++++++++++++ doc/cookbook/examples/cb-button.h | 72 +++++ 4 files changed, 440 insertions(+) create mode 100644 doc/cookbook/examples/actors-composite-main.c create mode 100644 doc/cookbook/examples/cb-button.c create mode 100644 doc/cookbook/examples/cb-button.h diff --git a/doc/cookbook/examples/Makefile.am b/doc/cookbook/examples/Makefile.am index 7021274b6..73342cdce 100644 --- a/doc/cookbook/examples/Makefile.am +++ b/doc/cookbook/examples/Makefile.am @@ -3,6 +3,7 @@ include $(top_srcdir)/build/autotools/Makefile.am.silent NULL = noinst_PROGRAMS = \ + actors-composite-main \ animations-complex \ animations-looping-animator \ animations-looping-implicit \ @@ -62,6 +63,7 @@ AM_CFLAGS = \ AM_LDFLAGS = $(CLUTTER_LIBS) -export-dynamic +actors_composite_main_SOURCES = cb-button.c actors-composite-main.c animations_complex_SOURCES = animations-complex.c animations_looping_animator_SOURCES = animations-looping-animator.c animations_looping_implicit_SOURCES = animations-looping-implicit.c diff --git a/doc/cookbook/examples/actors-composite-main.c b/doc/cookbook/examples/actors-composite-main.c new file mode 100644 index 000000000..217389424 --- /dev/null +++ b/doc/cookbook/examples/actors-composite-main.c @@ -0,0 +1,79 @@ +#include +#include "cb-button.h" + +/* colors */ +static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff }; +static const ClutterColor white_color = { 0xff, 0xff, 0xff, 0xff }; +static const ClutterColor yellow_color = { 0x88, 0x88, 0x00, 0xff }; + +/* click handler */ +static void +clicked (CbButton *button, + gpointer data) +{ + const gchar *current_text; + + g_debug ("Clicked"); + + current_text = cb_button_get_text (button); + + if (g_strcmp0 (current_text, "winkle") == 0) + cb_button_set_text (button, "pickers"); + else + cb_button_set_text (button, "winkle"); +} + +int +main (int argc, + char *argv[]) +{ + ClutterActor *stage; + ClutterActor *button; + ClutterConstraint *align_x_constraint; + ClutterConstraint *align_y_constraint; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); + + button = cb_button_new (); + cb_button_set_text (CB_BUTTON (button), "winkle"); + + /* the following is equivalent to the two lines above: + * + * button = g_object_new (CB_TYPE_BUTTON, + * "text", "winkle", + * NULL); + * + * because we defined a set_property function, which can accept + * a PROP_TEXT parameter, GObject can create a button and set one + * or more properties with a single call to g_object_new() + */ + + cb_button_set_text_color (CB_BUTTON (button), &white_color); + cb_button_set_background_color (CB_BUTTON (button), &yellow_color); + clutter_actor_set_size (button, 200, 100); + g_signal_connect (button, "clicked", G_CALLBACK (clicked), NULL); + + align_x_constraint = clutter_align_constraint_new (stage, + CLUTTER_ALIGN_X_AXIS, + 0.5); + + align_y_constraint = clutter_align_constraint_new (stage, + CLUTTER_ALIGN_Y_AXIS, + 0.5); + + clutter_actor_add_constraint (button, align_x_constraint); + clutter_actor_add_constraint (button, align_y_constraint); + + clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); + + clutter_actor_show (stage); + + clutter_main (); + + return EXIT_SUCCESS; +} diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c new file mode 100644 index 000000000..de45deebd --- /dev/null +++ b/doc/cookbook/examples/cb-button.c @@ -0,0 +1,287 @@ +#include "cb-button.h" +/* + * convenience macro for GType implementations; see: + * http://library.gnome.org/devel/gobject/2.27/gobject-Type-Information.html#G-DEFINE-TYPE:CAPS + */ +G_DEFINE_TYPE (CbButton, cb_button, CLUTTER_TYPE_ACTOR); + +/* macro for easy access to the private structure defined in this file */ +#define CB_BUTTON_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CB_TYPE_BUTTON, CbButtonPrivate)) + +/* private structure */ +struct _CbButtonPrivate +{ + ClutterActor *child; + ClutterActor *label; + ClutterAction *click_action; + gchar *text; +}; + +/* enum for signals */ +enum +{ + CLICKED, + LAST_SIGNAL +}; + +/* enum for properties */ +enum +{ + PROP_0, + PROP_TEXT +}; + +/* signals */ +static guint cb_button_signals[LAST_SIGNAL] = { 0, }; + +static void +cb_button_dispose (GObject *gobject) +{ + CbButtonPrivate *priv = CB_BUTTON (gobject)->priv; + + /* we just dispose of the child, and let its dispose() + * function deal with its children + */ + if (priv->child) + { + clutter_actor_unparent (priv->child); + priv->child = NULL; + } + + G_OBJECT_CLASS (cb_button_parent_class)->dispose (gobject); +} + +static void +cb_button_finalize (GObject *gobject) +{ + CbButtonPrivate *priv = CB_BUTTON (gobject)->priv; + + g_free (priv->text); + + G_OBJECT_CLASS (cb_button_parent_class)->finalize (gobject); +} + +static void +cb_button_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CbButton *button = CB_BUTTON (gobject); + + switch (prop_id) + { + case PROP_TEXT: + cb_button_set_text (button, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +cb_button_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CbButtonPrivate *priv = CB_BUTTON (gobject)->priv; + + switch (prop_id) + { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +/* ClutterActor implementation */ + +/* use the actor's allocation for the ClutterBox */ +static void +cb_button_allocate (ClutterActor *actor, + const ClutterActorBox *box, + ClutterAllocationFlags flags) +{ + CbButtonPrivate *priv = CB_BUTTON (actor)->priv; + ClutterActorBox child_box = { 0, }; + + /* set the allocation for the whole button */ + CLUTTER_ACTOR_CLASS (cb_button_parent_class)->allocate (actor, box, flags); + + /* make the child (the ClutterBox) fill the parent */ + child_box.x1 = 0.0; + child_box.y1 = 0.0; + child_box.x2 = clutter_actor_box_get_width (box); + child_box.y2 = clutter_actor_box_get_height (box); + + clutter_actor_allocate (priv->child, &child_box, flags); +} + +/* paint function implementation: just call paint() on the ClutterBox */ +static void +cb_button_paint (ClutterActor *actor) +{ + CbButtonPrivate *priv = CB_BUTTON (actor)->priv; + + clutter_actor_paint (priv->child); +} + +/* proxy ClickAction signals so they become signals from the actor */ +static void +cb_button_clicked (ClutterClickAction *action, + ClutterActor *actor, + gpointer user_data) +{ + g_signal_emit (actor, cb_button_signals[CLICKED], 0); +} + +/* GObject class and instance init */ +static void +cb_button_class_init (CbButtonClass *klass) +{ + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->dispose = cb_button_dispose; + gobject_class->finalize = cb_button_finalize; + gobject_class->set_property = cb_button_set_property; + gobject_class->get_property = cb_button_get_property; + + actor_class->allocate = cb_button_allocate; + actor_class->paint = cb_button_paint; + + g_type_class_add_private (klass, sizeof (CbButtonPrivate)); + + pspec = g_param_spec_string ("text", + "Text", + "Text of the button", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + + cb_button_signals[CLICKED] = + g_signal_new ("clicked", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CbButtonClass, clicked), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static void +cb_button_init (CbButton *self) +{ + CbButtonPrivate *priv; + ClutterLayoutManager *layout; + + priv = self->priv = CB_BUTTON_GET_PRIVATE (self); + + clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); + + layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, + CLUTTER_BIN_ALIGNMENT_CENTER); + + priv->child = clutter_box_new (layout); + + /* set the parent of the ClutterBox to this instance */ + clutter_actor_set_parent (priv->child, + CLUTTER_ACTOR (self)); + + /* add text label to the button */ + priv->label = g_object_new (CLUTTER_TYPE_TEXT, + "line-alignment", PANGO_ALIGN_CENTER, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + clutter_container_add_actor (CLUTTER_CONTAINER (priv->child), + priv->label); + + /* add a ClutterClickAction on this actor, so we can proxy its + * "clicked" signal through a signal from this actor + */ + priv->click_action = clutter_click_action_new (); + clutter_actor_add_action (CLUTTER_ACTOR (self), priv->click_action); + + g_signal_connect (priv->click_action, + "clicked", + G_CALLBACK (cb_button_clicked), + NULL); +} + +/* public API */ + +/* examples of public API functions which wrap functions + * on internal actors + */ +void +cb_button_set_text (CbButton *self, + const gchar *text) +{ + CbButtonPrivate *priv; + + /* public API should check its arguments; + * see also g_return_val_if_fail for functions which + * return a value + */ + g_return_if_fail (CB_IS_BUTTON (self)); + + priv = self->priv; + + g_free (priv->text); + + if (text) + priv->text = g_strdup (text); + else + priv->text = g_strdup (""); + + /* call the actual function on the ClutterText inside the layout */ + clutter_text_set_text (CLUTTER_TEXT (priv->label), priv->text); +} + +void +cb_button_set_background_color (CbButton *self, + const ClutterColor *color) +{ + g_return_if_fail (CB_IS_BUTTON (self)); + + clutter_box_set_color (CLUTTER_BOX (self->priv->child), color); +} + +void +cb_button_set_text_color (CbButton *self, + const ClutterColor *color) +{ + g_return_if_fail (CB_IS_BUTTON (self)); + + clutter_text_set_color (CLUTTER_TEXT (self->priv->label), color); +} + +/* see http://library.gnome.org/devel/glib/unstable/glib-Standard-Macros.html#G-CONST-RETURN:CAPS + * for an explanation of G_CONST_RETURN: basically it means that the + * return value of this function should not be modified + */ +G_CONST_RETURN gchar * +cb_button_get_text (CbButton *self) +{ + g_return_val_if_fail (CB_IS_BUTTON (self), NULL); + + return self->priv->text; +} + +ClutterActor * +cb_button_new (void) +{ + return g_object_new (CB_TYPE_BUTTON, NULL); +} diff --git a/doc/cookbook/examples/cb-button.h b/doc/cookbook/examples/cb-button.h new file mode 100644 index 000000000..fbf1992b7 --- /dev/null +++ b/doc/cookbook/examples/cb-button.h @@ -0,0 +1,72 @@ +/* inclusion guard */ +#ifndef __CB_BUTTON_H__ +#define __CB_BUTTON_H__ + +/* include any dependencies */ +#include + +/* GObject implementation */ + +/* declare this function signature to remove compilation errors with -Wall; + * the cb_button_get_type() function is actually added via the + * G_DEFINE_TYPE macro in the .c file + */ +GType cb_button_get_type (void); + +/* GObject type macros */ +#define CB_TYPE_BUTTON (cb_button_get_type ()) +#define CB_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CB_TYPE_BUTTON, CbButton)) +#define CB_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CB_TYPE_BUTTON)) +#define CB_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CB_TYPE_BUTTON, CbButtonClass)) +#define CB_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CB_TYPE_BUTTON)) +#define CB_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CB_TYPE_BUTTON, CbButtonClass)) + +/* + * Private instance fields; see + * http://www.gotw.ca/gotw/024.htm for the rationalse + */ +typedef struct _CbButtonPrivate CbButtonPrivate; +typedef struct _CbButton CbButton; +typedef struct _CbButtonClass CbButtonClass; + +/* object structure */ +struct _CbButton +{ + /**/ + ClutterActor parent_instance; + + /* structure containing private members */ + /**/ + CbButtonPrivate *priv; +}; + +/* class structure */ +struct _CbButtonClass +{ + /* signals */ + void (* clicked) (CbButton *button); + + /**/ + ClutterActorClass parent_class; +}; + +/* public API */ + +/* constructor - note this returns a ClutterActor instance */ +ClutterActor *cb_button_new (void); + +/* getter */ +G_CONST_RETURN gchar * cb_button_get_text (CbButton *self); + +/* setters - these are wrappers round functions + * which change properties of the internal actors + */ +void cb_button_set_text (CbButton *self, const gchar *text); + +void cb_button_set_background_color (CbButton *self, + const ClutterColor *color); + +void cb_button_set_text_color (CbButton *self, + const ClutterColor *color); + +#endif /* __CB_BUTTON_H__ */ From 7b9eaeeadbf6ff10b4e467d13a873685a613ba37 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 25 Jan 2011 11:36:13 +0000 Subject: [PATCH 02/18] docs: Add some gtk-doc annotations to example As this is a full GObject class implementation, add some gtk-doc annotations to demonstrate how how custom actor subclasses should be documented. --- doc/cookbook/examples/cb-button.c | 65 ++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index de45deebd..ead3c868f 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -1,15 +1,16 @@ #include "cb-button.h" + /* * convenience macro for GType implementations; see: * http://library.gnome.org/devel/gobject/2.27/gobject-Type-Information.html#G-DEFINE-TYPE:CAPS */ G_DEFINE_TYPE (CbButton, cb_button, CLUTTER_TYPE_ACTOR); -/* macro for easy access to the private structure defined in this file */ +/* macro for accessing the object's private structure */ #define CB_BUTTON_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CB_TYPE_BUTTON, CbButtonPrivate)) -/* private structure */ +/* private structure - should only be accessed through the public API */ struct _CbButtonPrivate { ClutterActor *child; @@ -18,14 +19,14 @@ struct _CbButtonPrivate gchar *text; }; -/* enum for signals */ +/* enumerates signal identifiers for this class */ enum { CLICKED, LAST_SIGNAL }; -/* enum for properties */ +/* enumerates property identifiers for this class */ enum { PROP_0, @@ -125,7 +126,7 @@ cb_button_allocate (ClutterActor *actor, clutter_actor_allocate (priv->child, &child_box, flags); } -/* paint function implementation: just call paint() on the ClutterBox */ +/* paint function implementation: just calls paint() on the ClutterBox */ static void cb_button_paint (ClutterActor *actor) { @@ -161,6 +162,11 @@ cb_button_class_init (CbButtonClass *klass) g_type_class_add_private (klass, sizeof (CbButtonPrivate)); + /** + * CbButton:text: + * + * The text shown on the #CbButton + */ pspec = g_param_spec_string ("text", "Text", "Text of the button", @@ -168,6 +174,13 @@ cb_button_class_init (CbButtonClass *klass) G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TEXT, pspec); + /** + * CbButton::clicked: + * @button: the #CbButton that emitted the signal + * + * The ::clicked signal is emitted when the internal #ClutterClickAction + * associated with a #CbButton emits its own ::clicked signal + */ cb_button_signals[CLICKED] = g_signal_new ("clicked", G_TYPE_FROM_CLASS (klass), @@ -221,10 +234,17 @@ cb_button_init (CbButton *self) } /* public API */ - /* examples of public API functions which wrap functions * on internal actors */ + +/** + * cb_button_set_text: + * @self: a #CbButton + * @text: the text to display on the button + * + * Set the text on the button + */ void cb_button_set_text (CbButton *self, const gchar *text) @@ -246,10 +266,17 @@ cb_button_set_text (CbButton *self, else priv->text = g_strdup (""); - /* call the actual function on the ClutterText inside the layout */ + /* call a function on the ClutterText inside the layout */ clutter_text_set_text (CLUTTER_TEXT (priv->label), priv->text); } +/** + * cb_button_set_background_color: + * @self: a #CbButton + * @color: the #ClutterColor to use for the button's background + * + * Set the color of the button's background + */ void cb_button_set_background_color (CbButton *self, const ClutterColor *color) @@ -259,6 +286,13 @@ cb_button_set_background_color (CbButton *self, clutter_box_set_color (CLUTTER_BOX (self->priv->child), color); } +/** + * cb_button_set_text_color: + * @self: a #CbButton + * @color: the #ClutterColor to use as the color for the button text + * + * Set the color of the text on the button + */ void cb_button_set_text_color (CbButton *self, const ClutterColor *color) @@ -268,9 +302,13 @@ cb_button_set_text_color (CbButton *self, clutter_text_set_color (CLUTTER_TEXT (self->priv->label), color); } -/* see http://library.gnome.org/devel/glib/unstable/glib-Standard-Macros.html#G-CONST-RETURN:CAPS - * for an explanation of G_CONST_RETURN: basically it means that the - * return value of this function should not be modified +/** + * cb_button_get_text: + * @self: a #CbButton + * + * Get the text displayed on the button + * + * Returns: the button's text. This must not be freed by the application. */ G_CONST_RETURN gchar * cb_button_get_text (CbButton *self) @@ -280,6 +318,13 @@ cb_button_get_text (CbButton *self) return self->priv->text; } +/** + * cb_button_new: + * + * Creates a new #CbButton instance + * + * Returns: a new #CbButton + */ ClutterActor * cb_button_new (void) { From b3954878c2827e78c0d800a038d6ae5cb515a136 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Wed, 26 Jan 2011 11:18:23 +0000 Subject: [PATCH 03/18] docs: Improve example code formatting Improve code formatting to adhere to Clutter uncrustify rules. --- doc/cookbook/examples/cb-button.c | 16 +++++++--------- doc/cookbook/examples/cb-button.h | 29 +++++++++++++++-------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index ead3c868f..bea999793 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -20,15 +20,13 @@ struct _CbButtonPrivate }; /* enumerates signal identifiers for this class */ -enum -{ +enum { CLICKED, LAST_SIGNAL }; /* enumerates property identifiers for this class */ -enum -{ +enum { PROP_0, PROP_TEXT }; @@ -107,9 +105,9 @@ cb_button_get_property (GObject *gobject, /* use the actor's allocation for the ClutterBox */ static void -cb_button_allocate (ClutterActor *actor, - const ClutterActorBox *box, - ClutterAllocationFlags flags) +cb_button_allocate (ClutterActor *actor, + const ClutterActorBox *box, + ClutterAllocationFlags flags) { CbButtonPrivate *priv = CB_BUTTON (actor)->priv; ClutterActorBox child_box = { 0, }; @@ -278,7 +276,7 @@ cb_button_set_text (CbButton *self, * Set the color of the button's background */ void -cb_button_set_background_color (CbButton *self, +cb_button_set_background_color (CbButton *self, const ClutterColor *color) { g_return_if_fail (CB_IS_BUTTON (self)); @@ -294,7 +292,7 @@ cb_button_set_background_color (CbButton *self, * Set the color of the text on the button */ void -cb_button_set_text_color (CbButton *self, +cb_button_set_text_color (CbButton *self, const ClutterColor *color) { g_return_if_fail (CB_IS_BUTTON (self)); diff --git a/doc/cookbook/examples/cb-button.h b/doc/cookbook/examples/cb-button.h index fbf1992b7..1d46cb096 100644 --- a/doc/cookbook/examples/cb-button.h +++ b/doc/cookbook/examples/cb-button.h @@ -14,20 +14,20 @@ GType cb_button_get_type (void); /* GObject type macros */ -#define CB_TYPE_BUTTON (cb_button_get_type ()) -#define CB_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CB_TYPE_BUTTON, CbButton)) -#define CB_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CB_TYPE_BUTTON)) -#define CB_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CB_TYPE_BUTTON, CbButtonClass)) -#define CB_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CB_TYPE_BUTTON)) -#define CB_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CB_TYPE_BUTTON, CbButtonClass)) +#define CB_TYPE_BUTTON (cb_button_get_type ()) +#define CB_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CB_TYPE_BUTTON, CbButton)) +#define CB_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CB_TYPE_BUTTON)) +#define CB_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CB_TYPE_BUTTON, CbButtonClass)) +#define CB_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CB_TYPE_BUTTON)) +#define CB_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CB_TYPE_BUTTON, CbButtonClass)) /* * Private instance fields; see - * http://www.gotw.ca/gotw/024.htm for the rationalse + * http://www.gotw.ca/gotw/024.htm for the rationale */ -typedef struct _CbButtonPrivate CbButtonPrivate; -typedef struct _CbButton CbButton; -typedef struct _CbButtonClass CbButtonClass; +typedef struct _CbButtonPrivate CbButtonPrivate; +typedef struct _CbButton CbButton; +typedef struct _CbButtonClass CbButtonClass; /* object structure */ struct _CbButton @@ -44,7 +44,7 @@ struct _CbButton struct _CbButtonClass { /* signals */ - void (* clicked) (CbButton *button); + void (* clicked)(CbButton *button); /**/ ClutterActorClass parent_class; @@ -61,12 +61,13 @@ G_CONST_RETURN gchar * cb_button_get_text (CbButton *self); /* setters - these are wrappers round functions * which change properties of the internal actors */ -void cb_button_set_text (CbButton *self, const gchar *text); +void cb_button_set_text (CbButton *self, + const gchar *text); -void cb_button_set_background_color (CbButton *self, +void cb_button_set_background_color (CbButton *self, const ClutterColor *color); -void cb_button_set_text_color (CbButton *self, +void cb_button_set_text_color (CbButton *self, const ClutterColor *color); #endif /* __CB_BUTTON_H__ */ From 7059be499ab8c31bcc9825f823587d05410f186a Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 11:13:47 +0000 Subject: [PATCH 04/18] docs: Add more comments on code example for composite actor Add more comments about the specific purpose of functions and variables in the composite actor example, particularly around GObject implementation. --- doc/cookbook/examples/cb-button.c | 69 +++++++++++++++++++++++++++---- doc/cookbook/examples/cb-button.h | 11 +++++ 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index bea999793..5b7b4bea0 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -1,7 +1,13 @@ #include "cb-button.h" -/* - * convenience macro for GType implementations; see: +/** + * SECTION:cb-button + * @short_description: Button widget + * + * A button widget with support for a text label and background color. + */ + +/* convenience macro for GType implementations; see: * http://library.gnome.org/devel/gobject/2.27/gobject-Type-Information.html#G-DEFINE-TYPE:CAPS */ G_DEFINE_TYPE (CbButton, cb_button, CLUTTER_TYPE_ACTOR); @@ -10,7 +16,13 @@ G_DEFINE_TYPE (CbButton, cb_button, CLUTTER_TYPE_ACTOR); #define CB_BUTTON_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CB_TYPE_BUTTON, CbButtonPrivate)) -/* private structure - should only be accessed through the public API */ +/* private structure - should only be accessed through the public API; + * this is used to store member variables whose properties + * need to be accessible from the implementation; for example, if we + * intend to create wrapper functions which modify properties on the + * actors composing an object, we should keep a reference to the actors + * here + */ struct _CbButtonPrivate { ClutterActor *child; @@ -31,16 +43,24 @@ enum { PROP_TEXT }; -/* signals */ +/* cache array for signals */ static guint cb_button_signals[LAST_SIGNAL] = { 0, }; +/* from http://mail.gnome.org/archives/gtk-devel-list/2004-July/msg00158.html: + * + * "The dispose method is supposed to release any references to resources + * when the object first knows it will be destroyed. The dispose method may + * be called any number of times, and thus the code therein should be safe + * in that case." + */ static void cb_button_dispose (GObject *gobject) { CbButtonPrivate *priv = CB_BUTTON (gobject)->priv; /* we just dispose of the child, and let its dispose() - * function deal with its children + * function deal with its children; note that we have a guard + * here in case the child has already been destroyed */ if (priv->child) { @@ -48,9 +68,19 @@ cb_button_dispose (GObject *gobject) priv->child = NULL; } + /* call the parent class' dispose() method */ G_OBJECT_CLASS (cb_button_parent_class)->dispose (gobject); } +/* from http://mail.gnome.org/archives/gtk-devel-list/2004-July/msg00158.html: + * + * "The finalize method finishes releasing the remaining + * resources just before the object itself will be freed from memory, and + * therefore it will only be called once. The two step process helps break + * cyclic references. Both dispose and finalize must chain up to their + * parent objects by calling their parent's respective methods *after* they + * have disposed or finalized their own members." + */ static void cb_button_finalize (GObject *gobject) { @@ -58,9 +88,14 @@ cb_button_finalize (GObject *gobject) g_free (priv->text); + /* call the parent class' finalize() method */ G_OBJECT_CLASS (cb_button_parent_class)->finalize (gobject); } +/* enables objects to be uniformly treated as GObjects; + * also exposes properties so they become scriptable, e.g. + * through ClutterScript + */ static void cb_button_set_property (GObject *gobject, guint prop_id, @@ -81,6 +116,7 @@ cb_button_set_property (GObject *gobject, } } +/* enables objects to be uniformly treated as GObjects */ static void cb_button_get_property (GObject *gobject, guint prop_id, @@ -139,10 +175,18 @@ cb_button_clicked (ClutterClickAction *action, ClutterActor *actor, gpointer user_data) { + /* emit signal via the cache array */ g_signal_emit (actor, cb_button_signals[CLICKED], 0); } -/* GObject class and instance init */ +/* GObject class and instance initialization functions; note that + * these have been placed after the Clutter implementation, as + * they refer to the static paint() and allocate() functions + */ + +/* class init: attach functions to the class, define properties + * and signals + */ static void cb_button_class_init (CbButtonClass *klass) { @@ -191,6 +235,9 @@ cb_button_class_init (CbButtonClass *klass) 0); } +/* object init: create a private structure and pack + * composed ClutterActors into it + */ static void cb_button_init (CbButton *self) { @@ -201,6 +248,10 @@ cb_button_init (CbButton *self) clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); + /* the only child of this actor is a ClutterBox with a + * ClutterBinLayout: painting and allocation of the actor basically + * involves painting and allocating this child box + */ layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER); @@ -210,7 +261,9 @@ cb_button_init (CbButton *self) clutter_actor_set_parent (priv->child, CLUTTER_ACTOR (self)); - /* add text label to the button */ + /* add text label to the button; see the ClutterText API docs + * for more information about available properties + */ priv->label = g_object_new (CLUTTER_TYPE_TEXT, "line-alignment", PANGO_ALIGN_CENTER, "ellipsize", PANGO_ELLIPSIZE_END, @@ -220,7 +273,7 @@ cb_button_init (CbButton *self) priv->label); /* add a ClutterClickAction on this actor, so we can proxy its - * "clicked" signal through a signal from this actor + * "clicked" signal into a signal from this actor */ priv->click_action = clutter_click_action_new (); clutter_actor_add_action (CLUTTER_ACTOR (self), priv->click_action); diff --git a/doc/cookbook/examples/cb-button.h b/doc/cookbook/examples/cb-button.h index 1d46cb096..9362a49ae 100644 --- a/doc/cookbook/examples/cb-button.h +++ b/doc/cookbook/examples/cb-button.h @@ -14,11 +14,22 @@ GType cb_button_get_type (void); /* GObject type macros */ +/* returns the class type identifier (GType) for CbButton */ #define CB_TYPE_BUTTON (cb_button_get_type ()) + +/* cast obj to a CbButton object structure*/ #define CB_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CB_TYPE_BUTTON, CbButton)) + +/* check whether obj is a CbButton */ #define CB_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CB_TYPE_BUTTON)) + +/* cast klass to CbButtonClass class structure */ #define CB_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CB_TYPE_BUTTON, CbButtonClass)) + +/* check whether klass is a member of the CbButtonClass */ #define CB_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CB_TYPE_BUTTON)) + +/* get the CbButtonClass structure for a CbButton obj */ #define CB_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CB_TYPE_BUTTON, CbButtonClass)) /* From 2adc224f0e701648ff0f08ed80317f4b3d4109df Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Wed, 26 Jan 2011 11:22:07 +0000 Subject: [PATCH 05/18] docs: Add recipe for creating a custom ClutterActor with composition --- doc/cookbook/actors.xml | 190 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/doc/cookbook/actors.xml b/doc/cookbook/actors.xml index d590fce02..5d944fcae 100644 --- a/doc/cookbook/actors.xml +++ b/doc/cookbook/actors.xml @@ -1,7 +1,8 @@ - + Actors @@ -37,6 +38,193 @@ +
+ Implementing a simple custom actor + +
+ Problem + + You want to implement your own ClutterActor. For + example, a widget (button, scrollbar, checkbox etc.) composed of + several Clutter primitives. +
+ +
+ Solution + + Implement a custom actor composed from a ClutterBox + packed with other ClutterActors. The custom actor + effectively provides a facade for these internal actors, simplifying + access to their properties and behavior. + + In this recipe, we make the simplest possible button widget + (CbButton) to demonstrate the basic principles of + implementing a ClutterActor. It is not a complete + button implementation: see + MxButton + for a more + comprehensive example (and the basis for this recipe). But + it does cover the most important parts of a ClutterActor + implementation, as well some useful GObject-related + code. + + The code for this solution is structured like standard GObject + C library code: + + + + The header file cb-button.h + declares the class' public API (function prototypes, macros, + structs). + + + The code file cb-button.c + contains the class implementation. + + + + One more example file, actors-composite-main.c, + shows how to use CbButton in an application. + + Each of these files is described in more detail below. + + + As Clutter is a GObject-based library, it relies + heavily on GObject concepts and idioms. If you are unfamiliar with GObject, + please read + the GObject + Reference Manual before proceeding. + + +
+ <filename>cb-button.h</filename> + + The header file defines the public API for the class, + including GObject type macros, class and object structures, and + function prototypes. + + + + a code sample should be here... but isn't + + + +
+ +
+ <filename>cb-button.c</filename> + + This is the main C code file which implements both + the GObject and Clutter parts of CbButton. + The example below is commented liberally, and gives some samples + of gtk-doc annotations to generate API docs for the + widget. The discussion section + comments more specifically about the Clutter-specific parts + of it. + + + enum for signals ???why do we need these + + enum for properties ???why do we need these + + array for signals ???is it an array + + + + + and Clutter stuff (private to the class): + + allocate (required) + paint (required) + you could get away with just these two + but if you're not composing your actor, or are creating a container, + you need to do more + + + + + then more GObject stuff (it should come after the Clutter method + implementations, i.e. allocate and paint): + + class_init + - where we wire together our functions + - we can also do things like adding properties and signals + + init + - where we create the private structure + - where we compose the actors together inside our ClutterActor + subclass + + + + + + Then normally some public API: + + typically you want a new() function + + for our class, we'll put some wrappers around functions + on the composed actors + + + +
+ +
+ <filename>actors-composite-main.c</filename> + + + The cb-button-main.c file shows how to use the new + actor inside a Clutter application. Note how any of the + ClutterActor functions (like set_size, + add_constraint) can be applied to instance + of our ClutterActor implementation. + + +
+ +
+ +
+ Discussion + + Explain about GObject a bit??? + + The actor implemented here is put together through + simple composition. This has the advantage of simplifying the + code for the subclass: we can just wrap a facade round + existing Clutter classes, rather than writing code to implement + everything ourselves. In the example here, we make use of a + ClutterLayoutManager to handle positioning of + the ClutterText; we change the background color of + the button by changing the color of the + ClutterBox; we use a ClutterClickAction + to simplify implementation of a click signal. + + On the other hand, it puts some constraints on the outline of + the actor: it makes it harder to use a custom outline, for + example, to round the corners of the button. This approach may + not be suitable where you need to do a lot of custom animation + and drawing. + + This isn't the whole story: if you aren't using this simple + composition approach, you may need to do more: see the notes in + the Clutter reference manual. Also, if you're implementing a + container, you'll need to do more. + + use Mx for inspiration??? + + GObject implementation parts vs. Clutter implementation??? + + something about how the ClutterBox is parented onto the + ClutterActor subclass we are implementing; we just create + an allocation for the ClutterBox based on the allocation of its + parent??? + +
+ +
+
Knowing when an actor's position or size changes From 951f13bb8dc2ead8fe3fd3a6f2fede2618c57a19 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 11:35:14 +0000 Subject: [PATCH 06/18] docs: Add example of preferred_height() and preferred_width() As most actor subclasses will probably want to implement size requisition, give a simple example of how to do this on the basis of the composed actor's size, plus some padding. --- doc/cookbook/examples/cb-button.c | 67 +++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index 5b7b4bea0..0cf57bc00 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -137,7 +137,12 @@ cb_button_get_property (GObject *gobject, } } -/* ClutterActor implementation */ +/* ClutterActor implementation + * + * we only implement allocate(), paint(), get_preferred_height() + * and get_preferred_width(), as this is the minimum + * we can get away with + */ /* use the actor's allocation for the ClutterBox */ static void @@ -169,6 +174,60 @@ cb_button_paint (ClutterActor *actor) clutter_actor_paint (priv->child); } +/* get_preferred_height defers to the internal ClutterBox + * but adds 20px padding around it; + * min_height_p is the minimum height the actor should occupy + * to be useful; natural_height_p is the height the actor + * would occupy if not constrained + * + * note that if we required explicit sizing for CbButtons + * (i.e. a developer must set their height and width), + * we wouldn't need to implement this function + */ +static void +cb_button_get_preferred_height (ClutterActor *self, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + CbButtonPrivate *priv = CB_BUTTON (self)->priv; + + clutter_actor_get_preferred_height (priv->child, + for_width, + min_height_p, + natural_height_p); + + *min_height_p += 20.0; + *natural_height_p += 20.0; +} + +/* get_preferred_width defers to the internal ClutterBox + * but adds 20px padding around it; + * min_width_p is the minimum width the actor should occupy + * to be useful; natural_width_p is the width the actor + * would occupy if not constrained + * + * note that if we required explicit sizing for CbButtons + * (i.e. a developer must set their height and width), + * we wouldn't need to implement this function + */ +static void +cb_button_get_preferred_width (ClutterActor *self, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + CbButtonPrivate *priv = CB_BUTTON (self)->priv; + + clutter_actor_get_preferred_width (priv->child, + for_height, + min_width_p, + natural_width_p); + + *min_width_p += 20.0; + *natural_width_p += 20.0; +} + /* proxy ClickAction signals so they become signals from the actor */ static void cb_button_clicked (ClutterClickAction *action, @@ -181,10 +240,10 @@ cb_button_clicked (ClutterClickAction *action, /* GObject class and instance initialization functions; note that * these have been placed after the Clutter implementation, as - * they refer to the static paint() and allocate() functions + * they refer to the static function implementations above */ -/* class init: attach functions to the class, define properties +/* class init: attach functions to superclasses, define properties * and signals */ static void @@ -201,6 +260,8 @@ cb_button_class_init (CbButtonClass *klass) actor_class->allocate = cb_button_allocate; actor_class->paint = cb_button_paint; + actor_class->get_preferred_height = cb_button_get_preferred_height; + actor_class->get_preferred_width = cb_button_get_preferred_width; g_type_class_add_private (klass, sizeof (CbButtonPrivate)); From 18b90f100c259db2e5ef19874277b3d5d50791f0 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 11:36:20 +0000 Subject: [PATCH 07/18] docs: Don't set explicit size on button Rather than set a size on the CbButton instance, let it size itself automatically, based on the size requisition functions. --- doc/cookbook/examples/actors-composite-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cookbook/examples/actors-composite-main.c b/doc/cookbook/examples/actors-composite-main.c index 217389424..770b8b3d4 100644 --- a/doc/cookbook/examples/actors-composite-main.c +++ b/doc/cookbook/examples/actors-composite-main.c @@ -53,9 +53,9 @@ main (int argc, * or more properties with a single call to g_object_new() */ + /* note that the size of the button is left to Clutter's size requisition */ cb_button_set_text_color (CB_BUTTON (button), &white_color); cb_button_set_background_color (CB_BUTTON (button), &yellow_color); - clutter_actor_set_size (button, 200, 100); g_signal_connect (button, "clicked", G_CALLBACK (clicked), NULL); align_x_constraint = clutter_align_constraint_new (stage, From 5f676ce3253f4e6f5ac07a1c1099d17e8384d5ee Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 11:39:26 +0000 Subject: [PATCH 08/18] docs: Explain enums for properties and signals Add some more explanatory comments about the PROP_ and signals enums. --- doc/cookbook/examples/cb-button.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index 0cf57bc00..d16874357 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -31,18 +31,24 @@ struct _CbButtonPrivate gchar *text; }; -/* enumerates signal identifiers for this class */ -enum { - CLICKED, - LAST_SIGNAL -}; - -/* enumerates property identifiers for this class */ +/* enumerates property identifiers for this class; + * note that property identifiers should be non-zero integers, + * so we add an unused PROP_0 to occupy the 0 position in the enum + */ enum { PROP_0, PROP_TEXT }; +/* enumerates signal identifiers for this class; + * LAST_SIGNAL is not used as a signal identifier, but is instead + * used to delineate the size of the cache array for signals (see below) + */ +enum { + CLICKED, + LAST_SIGNAL +}; + /* cache array for signals */ static guint cb_button_signals[LAST_SIGNAL] = { 0, }; From 00deb59a9daa45558753810fcc18c738f61b0a6e Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 11:49:08 +0000 Subject: [PATCH 09/18] docs: Include code examples in the recipe Include all the code examples inline as part of the recipe. Remove sections around each code example, as these are unnecessary; leave full discussion for the Discussion section instead of trying to cram it in around the code example. --- doc/cookbook/actors.xml | 103 +++++++++++++--------------------------- 1 file changed, 34 insertions(+), 69 deletions(-) diff --git a/doc/cookbook/actors.xml b/doc/cookbook/actors.xml index 5d944fcae..3fd3a6a8e 100644 --- a/doc/cookbook/actors.xml +++ b/doc/cookbook/actors.xml @@ -96,92 +96,55 @@ Reference Manual before proceeding. -
- <filename>cb-button.h</filename> + + <filename>cb-button.h</filename>: header file - The header file defines the public API for the class, - including GObject type macros, class and object structures, and + This defines the public API for the class, including + GObject type macros, class and object structures, and function prototypes. - a code sample should be here... but isn't +a code sample should be here... but isn't + -
- -
- <filename>cb-button.c</filename> + + <filename>cb-button.c</filename>: <type>ClutterActor</type> + and GObject implementation This is the main C code file which implements both the GObject and Clutter parts of CbButton. The example below is commented liberally, and gives some samples of gtk-doc annotations to generate API docs for the - widget. The discussion section - comments more specifically about the Clutter-specific parts - of it. + widget. The discussion + section comments more specifically about the Clutter-specific + parts of it. - - enum for signals ???why do we need these + + +a code sample should be here... but isn't + + + - enum for properties ???why do we need these + + <filename>actors-composite-main.c</filename>: trivial + application demonstrating usage of <type>CbButton</type> - array for signals ???is it an array + Note how any of the ClutterActor + functions (like clutter_actor_set_size() + and clutter_actor_add_constraint()) can + be applied to instances of our ClutterActor + implementation. - - - - and Clutter stuff (private to the class): - - allocate (required) - paint (required) - you could get away with just these two - but if you're not composing your actor, or are creating a container, - you need to do more - - - - - then more GObject stuff (it should come after the Clutter method - implementations, i.e. allocate and paint): - - class_init - - where we wire together our functions - - we can also do things like adding properties and signals - - init - - where we create the private structure - - where we compose the actors together inside our ClutterActor - subclass - - - - - - Then normally some public API: - - typically you want a new() function - - for our class, we'll put some wrappers around functions - on the composed actors - - - -
- -
- <filename>actors-composite-main.c</filename> - - - The cb-button-main.c file shows how to use the new - actor inside a Clutter application. Note how any of the - ClutterActor functions (like set_size, - add_constraint) can be applied to instance - of our ClutterActor implementation. - - -
+ + +a code sample should be here... but isn't + + +
@@ -190,6 +153,8 @@ Explain about GObject a bit??? + Note size requisition in the example application??? + The actor implemented here is put together through simple composition. This has the advantage of simplifying the code for the subclass: we can just wrap a facade round From 6934b3645164b5e2a4d4c3da5ed99fe93bb9a95d Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 16:43:22 +0000 Subject: [PATCH 10/18] docs: Add more comments on how allocate() works Add some extra description to the allocate() function, explaining how the allocation has to be adjusted to coordinates relative to the actor as a whole, before applying to the single child actor it is composed from. --- doc/cookbook/examples/cb-button.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index d16874357..20ee6e5c1 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -162,7 +162,14 @@ cb_button_allocate (ClutterActor *actor, /* set the allocation for the whole button */ CLUTTER_ACTOR_CLASS (cb_button_parent_class)->allocate (actor, box, flags); - /* make the child (the ClutterBox) fill the parent */ + /* make the child (the ClutterBox) fill the parent; + * note that this allocation box is relative to the + * coordinates of the whole button actor, so we can't just + * use the box passed into this function; instead, it + * is adjusted to span the whole of the actor, from its + * top-left corner (0,0) to its bottom-right corner + * (width,height) + */ child_box.x1 = 0.0; child_box.y1 = 0.0; child_box.x2 = clutter_actor_box_get_width (box); From 238fd52c4b0660ac61b3ae125f3eef8f71b0f6b0 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Fri, 28 Jan 2011 17:09:24 +0000 Subject: [PATCH 11/18] docs: Change order of functions in example to match docs Moved the functions around in the C code file, to match the order Clutter uses them, and the order they are explained in the recipe. --- doc/cookbook/examples/cb-button.c | 103 +++++++++++++----------------- 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index 20ee6e5c1..49543b653 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -145,11 +145,54 @@ cb_button_get_property (GObject *gobject, /* ClutterActor implementation * - * we only implement allocate(), paint(), get_preferred_height() - * and get_preferred_width(), as this is the minimum - * we can get away with + * we only implement get_preferred_height(), get_preferred_width(), + * allocate(), and paint(), as this is the minimum we can get away with */ +/* get_preferred_height and get_preferred_width defer to the + * internal ClutterBox, adding 20px padding on each axis; + * min_*_p is the minimum height or width the actor should occupy + * to be useful; natural_*_p is the height or width the actor + * would occupy if not constrained + * + * note that if we required explicit sizing for CbButtons + * (i.e. a developer must set their height and width), + * we wouldn't need to implement these functions + */ +static void +cb_button_get_preferred_height (ClutterActor *self, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) +{ + CbButtonPrivate *priv = CB_BUTTON (self)->priv; + + clutter_actor_get_preferred_height (priv->child, + for_width, + min_height_p, + natural_height_p); + + *min_height_p += 20.0; + *natural_height_p += 20.0; +} + +static void +cb_button_get_preferred_width (ClutterActor *self, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + CbButtonPrivate *priv = CB_BUTTON (self)->priv; + + clutter_actor_get_preferred_width (priv->child, + for_height, + min_width_p, + natural_width_p); + + *min_width_p += 20.0; + *natural_width_p += 20.0; +} + /* use the actor's allocation for the ClutterBox */ static void cb_button_allocate (ClutterActor *actor, @@ -187,60 +230,6 @@ cb_button_paint (ClutterActor *actor) clutter_actor_paint (priv->child); } -/* get_preferred_height defers to the internal ClutterBox - * but adds 20px padding around it; - * min_height_p is the minimum height the actor should occupy - * to be useful; natural_height_p is the height the actor - * would occupy if not constrained - * - * note that if we required explicit sizing for CbButtons - * (i.e. a developer must set their height and width), - * we wouldn't need to implement this function - */ -static void -cb_button_get_preferred_height (ClutterActor *self, - gfloat for_width, - gfloat *min_height_p, - gfloat *natural_height_p) -{ - CbButtonPrivate *priv = CB_BUTTON (self)->priv; - - clutter_actor_get_preferred_height (priv->child, - for_width, - min_height_p, - natural_height_p); - - *min_height_p += 20.0; - *natural_height_p += 20.0; -} - -/* get_preferred_width defers to the internal ClutterBox - * but adds 20px padding around it; - * min_width_p is the minimum width the actor should occupy - * to be useful; natural_width_p is the width the actor - * would occupy if not constrained - * - * note that if we required explicit sizing for CbButtons - * (i.e. a developer must set their height and width), - * we wouldn't need to implement this function - */ -static void -cb_button_get_preferred_width (ClutterActor *self, - gfloat for_height, - gfloat *min_width_p, - gfloat *natural_width_p) -{ - CbButtonPrivate *priv = CB_BUTTON (self)->priv; - - clutter_actor_get_preferred_width (priv->child, - for_height, - min_width_p, - natural_width_p); - - *min_width_p += 20.0; - *natural_width_p += 20.0; -} - /* proxy ClickAction signals so they become signals from the actor */ static void cb_button_clicked (ClutterClickAction *action, From 08f5dc08d0e11e2f03b3a9c8889222a584cbe0e7 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 10:53:52 +0000 Subject: [PATCH 12/18] docs: Complete composite actor recipe Add some extra detail to the Discussion section of the composite actor recipe, concentrating on the pros and cons of this approach. Also explain more about the Clutter parts of the implementation. Also general tidy up of language and style. --- doc/cookbook/actors.xml | 220 +++++++++++++++++++++++++++++++--------- 1 file changed, 170 insertions(+), 50 deletions(-) diff --git a/doc/cookbook/actors.xml b/doc/cookbook/actors.xml index 3fd3a6a8e..35b202c55 100644 --- a/doc/cookbook/actors.xml +++ b/doc/cookbook/actors.xml @@ -44,9 +44,10 @@
Problem - You want to implement your own ClutterActor. For - example, a widget (button, scrollbar, checkbox etc.) composed of - several Clutter primitives. + You want to implement your own ClutterActor; + for example, a very simple button widget. But you want to base it + on existing Clutter primitives (rectangles, text) to minimise + the work required.
@@ -54,19 +55,25 @@ Implement a custom actor composed from a ClutterBox packed with other ClutterActors. The custom actor - effectively provides a facade for these internal actors, simplifying + provides a facade over these internal actors, simplifying access to their properties and behavior. - In this recipe, we make the simplest possible button widget - (CbButton) to demonstrate the basic principles of - implementing a ClutterActor. It is not a complete - button implementation: see - MxButton - for a more - comprehensive example (and the basis for this recipe). But - it does cover the most important parts of a ClutterActor - implementation, as well some useful GObject-related - code. + In this recipe, we subclass ClutterActor using this + approach to create a very simple button widget, CbButton. + It is not a complete button implementation: see + + MxButton for a more comprehensive example + (and the basis for this recipe). But this recipe does cover the most + important parts of a ClutterActor implementation, + as well some useful GObject-related code. + + + As Clutter is a GObject-based library, it relies + heavily on GObject concepts and idioms. If you are unfamiliar with + GObject, please read + the GObject + Reference Manual before proceeding. + The code for this solution is structured like standard GObject C library code: @@ -88,13 +95,19 @@ Each of these files is described in more detail below. - - As Clutter is a GObject-based library, it relies - heavily on GObject concepts and idioms. If you are unfamiliar with GObject, - please read - the GObject - Reference Manual before proceeding. - + + In a more realistic context, CbButton would + have some build infrastructure (for example, autotooling) + so it could be compiled, installed, and reused in a variety of + applications. However, for the purposes of cookbook examples, + these issues are ignored here. + + If you are planning on building + your own widgets using Clutter as part of an application, or + to create your own library, the + Mx toolkit + provides an excellent example of how to autotool your project. + <filename>cb-button.h</filename>: header file @@ -115,10 +128,12 @@ and GObject implementation This is the main C code file which implements both - the GObject and Clutter parts of CbButton. - The example below is commented liberally, and gives some samples - of gtk-doc annotations to generate API docs for the - widget. The discussion + the GObject and Clutter elements of CbButton. + The example below is liberally commented, and also gives some samples + of annotations to generate + gtk-docs for the + widget. The + discussion section comments more specifically about the Clutter-specific parts of it. @@ -151,40 +166,145 @@
Discussion - Explain about GObject a bit??? - - Note size requisition in the example application??? - - The actor implemented here is put together through - simple composition. This has the advantage of simplifying the - code for the subclass: we can just wrap a facade round - existing Clutter classes, rather than writing code to implement - everything ourselves. In the example here, we make use of a + The actor implemented here is based on + simple composition: bundling several actors together and wrapping + their behavior and properties. In the example here, we make use of a ClutterLayoutManager to handle positioning of the ClutterText; we change the background color of the button by changing the color of the - ClutterBox; we use a ClutterClickAction + ClutterBox; and we use a ClutterClickAction to simplify implementation of a click signal. - On the other hand, it puts some constraints on the outline of - the actor: it makes it harder to use a custom outline, for - example, to round the corners of the button. This approach may - not be suitable where you need to do a lot of custom animation - and drawing. + You may find that this approach is appropriate if you need + to implement a simple rectangular actor. However, it puts some + constraints on the outline of the actor, making it harder to + use a custom outline: for example, a rectangle with rounded corners + or a shape which can't be approximated by a rectangle. Such cases + require both pick() and paint() + implementations using Cogl (or similar): see + this recipe + for more details. - This isn't the whole story: if you aren't using this simple - composition approach, you may need to do more: see the notes in - the Clutter reference manual. Also, if you're implementing a - container, you'll need to do more. + The composition approach may also be inappropriate where + you need to do a lot of custom animation and drawing; and it is + likely to be inappropriate for implementing a container + actor. See the notes on implementing a new actor in the Clutter + reference manual for more details of what may be required + in these cases. - use Mx for inspiration??? +
+ Implementing <type>ClutterActor</type> virtual functions - GObject implementation parts vs. Clutter implementation??? + While most of the CbButton implementation + revolves around GObject, there are some elements of it + specific to Clutter. Due to the simplicity of + the CbButton actor, the implementation of + these functions is fairly trivial, as explained below: - something about how the ClutterBox is parented onto the - ClutterActor subclass we are implementing; we just create - an allocation for the ClutterBox based on the allocation of its - parent??? + + + + + + Size requisition: + <function>cb_button_get_preferred_height()</function> + and <function>cb_button_get_preferred_width()</function> + + During the size requisition phase, Clutter asks each + actor the minimum size it should be to remain useful, + and the maximum size it would be if unconstrained. This is done + by calling the get_preferred_height() + and get_preferred_width() functions + on each actor in turn. + + + If an actor will only ever be explictly sized + (via clutter_actor_set_size(), + clutter_actor_set_height() and/or + clutter_actor_set_width()), + there is no need to implement the get_preferred_*() + functions. (Some actors like ClutterRectangle + work this way and require explicit sizing.) + + However, if an actor's size should be negotiated during + the size requisition phase, you can implement these functions, + using the size of the child actors as a basis for the + preferred height and width. In the case of + CbButton, a preferred height and width can be + computed; these are based on the height and width of + the child ClutterBox, plus 20 pixels on each + axis. Because the size of the box is itself dependent on + the size of the ClutterText inside it, the net + result is that the CbButton preferred size + is the size of the text actor inside it, plus 20 pixels on each + axis. + + + + + + + Allocation: + <function>cb_button_allocate()</function> + + The requests gathered during size requisition + are then negotiated by Clutter, each actor + receiving some allocation of the available space. At the + end of this process, each actor is allocated a + box, representing the space available + to it on the stage. + + + An actor implementation is responsible for distributing + space from its allocation box to its children as it sees + fit. In the case of CbButton, there is only a single + ClutterBox actor which needs allocation; + cb_button_allocate() therefore + allocates all of the button's space to its child + ClutterBox. + + + + + + + Painting and picking: + <function>cb_button_paint()</function> + + Clutter works its way through the actors on the + stage, following the actor hierarchy (top level + actors directly inside the stage first); + clutter_actor_paint() + is called on each actor. This, in turn, calls the actor's + paint() implementation. If the actor + is a container, it may iterate over its children, + calling paint() on each; the children + may call paint() on their children...; + and so on, until the leaves of the actor hierarchy are + reached. + + + As our actor consists of a single ClutterBox + child, its paint() implementation simply + has to retrieve the reference to that ClutterBox + (from its private structure) and call + clutter_actor_paint() + on it. Painting of the ClutterBox's child + (the ClutterText) is handled by the + ClutterBox. + + In cases where an actor is non-rectangular, you also + need to implement a pick() function. + (This is used to determine which actor was the recipient of + an event occurring within the stage.) However, because + the actor in this recipe is a simple rectangle, there is no + need to implement pick(). + + + + + +
From 5530c5e2ec9605722e7f65b60b73e8fe396e6c39 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 11:05:27 +0000 Subject: [PATCH 13/18] docs: Add a note about other state variables Explain that the private structure would be the place to store other state variables for the instance. --- doc/cookbook/examples/cb-button.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index 49543b653..fd579da5c 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -22,6 +22,10 @@ G_DEFINE_TYPE (CbButton, cb_button, CLUTTER_TYPE_ACTOR); * intend to create wrapper functions which modify properties on the * actors composing an object, we should keep a reference to the actors * here + * + * this is also the place where other state variables go: + * for example, you might record the current state of the button + * (toggled on or off) or a background image */ struct _CbButtonPrivate { From 3f64137a7937498ea4982edf9c1fb133fe9321e4 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 11:06:01 +0000 Subject: [PATCH 14/18] docs: Change text on button Modify the text shown on the button to "hello / world" rather than "winkle / pickers". Slightly more sensible. --- doc/cookbook/examples/actors-composite-main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/cookbook/examples/actors-composite-main.c b/doc/cookbook/examples/actors-composite-main.c index 770b8b3d4..a54efbe37 100644 --- a/doc/cookbook/examples/actors-composite-main.c +++ b/doc/cookbook/examples/actors-composite-main.c @@ -17,10 +17,10 @@ clicked (CbButton *button, current_text = cb_button_get_text (button); - if (g_strcmp0 (current_text, "winkle") == 0) - cb_button_set_text (button, "pickers"); + if (g_strcmp0 (current_text, "hello") == 0) + cb_button_set_text (button, "world"); else - cb_button_set_text (button, "winkle"); + cb_button_set_text (button, "hello"); } int @@ -40,7 +40,7 @@ main (int argc, g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); button = cb_button_new (); - cb_button_set_text (CB_BUTTON (button), "winkle"); + cb_button_set_text (CB_BUTTON (button), "hello"); /* the following is equivalent to the two lines above: * From f63158c2a2c27e7d4a264caf994f799a38fb6398 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 12:18:58 +0000 Subject: [PATCH 15/18] docs: Don't use clutter_stage_get_default() clutter_stage_get_new() is the recommended way to get a stage instance, so use that instead. --- doc/cookbook/examples/actors-composite-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cookbook/examples/actors-composite-main.c b/doc/cookbook/examples/actors-composite-main.c index a54efbe37..2df5fa90a 100644 --- a/doc/cookbook/examples/actors-composite-main.c +++ b/doc/cookbook/examples/actors-composite-main.c @@ -34,7 +34,7 @@ main (int argc, clutter_init (&argc, &argv); - stage = clutter_stage_get_default (); + stage = clutter_stage_new (); clutter_actor_set_size (stage, 400, 400); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); From e69d60e8b64bbe0811479dac96aee6009560be4e Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 13:36:37 +0000 Subject: [PATCH 16/18] docs: Implement destroy() rather than dispose() Remove the dispose() implementation and replace with destroy(). This should be promoted as the standard approach for implementing a composite actor, as it emits a signal when instances of the actor subclass are destroyed. --- doc/cookbook/examples/cb-button.c | 61 ++++++++++++++++--------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c index fd579da5c..0260d1470 100644 --- a/doc/cookbook/examples/cb-button.c +++ b/doc/cookbook/examples/cb-button.c @@ -56,32 +56,6 @@ enum { /* cache array for signals */ static guint cb_button_signals[LAST_SIGNAL] = { 0, }; -/* from http://mail.gnome.org/archives/gtk-devel-list/2004-July/msg00158.html: - * - * "The dispose method is supposed to release any references to resources - * when the object first knows it will be destroyed. The dispose method may - * be called any number of times, and thus the code therein should be safe - * in that case." - */ -static void -cb_button_dispose (GObject *gobject) -{ - CbButtonPrivate *priv = CB_BUTTON (gobject)->priv; - - /* we just dispose of the child, and let its dispose() - * function deal with its children; note that we have a guard - * here in case the child has already been destroyed - */ - if (priv->child) - { - clutter_actor_unparent (priv->child); - priv->child = NULL; - } - - /* call the parent class' dispose() method */ - G_OBJECT_CLASS (cb_button_parent_class)->dispose (gobject); -} - /* from http://mail.gnome.org/archives/gtk-devel-list/2004-July/msg00158.html: * * "The finalize method finishes releasing the remaining @@ -149,10 +123,37 @@ cb_button_get_property (GObject *gobject, /* ClutterActor implementation * - * we only implement get_preferred_height(), get_preferred_width(), + * we only implement destroy(), get_preferred_height(), get_preferred_width(), * allocate(), and paint(), as this is the minimum we can get away with */ +/* composite actors should implement destroy(), and inside their + * implementation destroy any actors they are composed from; + * in this case, we just destroy the child ClutterBox + */ +static void +cb_button_destroy (ClutterActor *self) +{ + CbButtonPrivate *priv = CB_BUTTON (self)->priv; + + /* we just destroy the child, and let the child + * deal with destroying _its_ children; note that we have a guard + * here in case the child has already been destroyed + */ + if (priv->child) + { + clutter_actor_destroy (priv->child); + priv->child = NULL; + } + + /* chain up to destroy() on the parent ClutterActorClass; + * note that we check the parent class has a destroy() implementation + * before calling it + */ + if (CLUTTER_ACTOR_CLASS (cb_button_parent_class)->destroy) + CLUTTER_ACTOR_CLASS (cb_button_parent_class)->destroy (self); +} + /* get_preferred_height and get_preferred_width defer to the * internal ClutterBox, adding 20px padding on each axis; * min_*_p is the minimum height or width the actor should occupy @@ -259,15 +260,15 @@ cb_button_class_init (CbButtonClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; - gobject_class->dispose = cb_button_dispose; gobject_class->finalize = cb_button_finalize; gobject_class->set_property = cb_button_set_property; gobject_class->get_property = cb_button_get_property; - actor_class->allocate = cb_button_allocate; - actor_class->paint = cb_button_paint; + actor_class->destroy = cb_button_destroy; actor_class->get_preferred_height = cb_button_get_preferred_height; actor_class->get_preferred_width = cb_button_get_preferred_width; + actor_class->allocate = cb_button_allocate; + actor_class->paint = cb_button_paint; g_type_class_add_private (klass, sizeof (CbButtonPrivate)); From 885664f725aa3704c46db62e7b1fa951819fc22c Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 13:39:11 +0000 Subject: [PATCH 17/18] docs: Explain why destroy() is implemented As destroy() is Clutter-specific and not generic GObject code, explain why we implement it (rather than dispose()). --- doc/cookbook/actors.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/cookbook/actors.xml b/doc/cookbook/actors.xml index 35b202c55..1a307f085 100644 --- a/doc/cookbook/actors.xml +++ b/doc/cookbook/actors.xml @@ -203,6 +203,31 @@ + + + + Object destruction: + <function>cb_button_destroy()</function> + + ClutterActor subclasses based + on composition should implement the destroy() + virtual function. This is called on an actor when its + container is destroyed to clean up the resources + allocated to the actor; it also emits a + destroy signal which other code can + hook onto. + + + In the case of CbButton, the + destroy() implementation calls + clutter_actor_destroy() on the child + ClutterBox, then sets that child to + NULL. Finally, it checks for a + destroy() implementation on the parent + class, then calls it if one exists. + + + From 6680cebfe1f0ef697e16f6db26d1e7fb70d5e459 Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Mon, 31 Jan 2011 13:40:10 +0000 Subject: [PATCH 18/18] docs: Add reference to useful GObject tutorial Add a reference to a GObject tutorial which is a good introduction to the various macros etc. and what they're for. --- doc/cookbook/actors.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/cookbook/actors.xml b/doc/cookbook/actors.xml index 1a307f085..c1fc63fcc 100644 --- a/doc/cookbook/actors.xml +++ b/doc/cookbook/actors.xml @@ -72,7 +72,9 @@ heavily on GObject concepts and idioms. If you are unfamiliar with GObject, please read the GObject - Reference Manual before proceeding. + Reference Manual before proceeding. You might also find + this + tutorial a useful introduction. The code for this solution is structured like standard GObject