docs: Add custom ClutterActor example which uses composition
authorElliot Smith <elliot.smith@intel.com>
Fri, 21 Jan 2011 16:43:03 +0000 (16:43 +0000)
committerElliot Smith <elliot.smith@intel.com>
Mon, 31 Jan 2011 10:08:07 +0000 (10:08 +0000)
doc/cookbook/examples/Makefile.am
doc/cookbook/examples/actors-composite-main.c [new file with mode: 0644]
doc/cookbook/examples/cb-button.c [new file with mode: 0644]
doc/cookbook/examples/cb-button.h [new file with mode: 0644]

index 7021274..73342cd 100644 (file)
@@ -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 (file)
index 0000000..2173894
--- /dev/null
@@ -0,0 +1,79 @@
+#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;
+}
diff --git a/doc/cookbook/examples/cb-button.c b/doc/cookbook/examples/cb-button.c
new file mode 100644 (file)
index 0000000..de45dee
--- /dev/null
@@ -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 (file)
index 0000000..fbf1992
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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__ */