--- /dev/null
+#include <stdlib.h>
+#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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+/* inclusion guard */
+#ifndef __CB_BUTTON_H__
+#define __CB_BUTTON_H__
+
+/* include any dependencies */
+#include <clutter/clutter.h>
+
+/* 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
+{
+ /*<private>*/
+ ClutterActor parent_instance;
+
+ /* structure containing private members */
+ /*<private>*/
+ CbButtonPrivate *priv;
+};
+
+/* class structure */
+struct _CbButtonClass
+{
+ /* signals */
+ void (* clicked) (CbButton *button);
+
+ /*<private>*/
+ 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__ */