[clutter] examples: Add an example of layout manager



commit 4d087f2c0ada4c7f5f6a9bd1aa86b337c05e9f1b
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Fri May 4 17:43:30 2012 +0100

    examples: Add an example of layout manager
    
    The MultiLayout shows how to write a layout manager with two policies,
    and to use the easing state of a child to interpolate the allocation.

 examples/Makefile.am      |    1 +
 examples/layout-manager.c |  409 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 410 insertions(+), 0 deletions(-)
---
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 260c5d9..8505847 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -9,6 +9,7 @@ all_examples = \
 	drop-action \
 	easing-modes \
 	flow-layout \
+	layout-manager \
 	rounded-rectangle \
 	threads
 
diff --git a/examples/layout-manager.c b/examples/layout-manager.c
new file mode 100644
index 0000000..8e91022
--- /dev/null
+++ b/examples/layout-manager.c
@@ -0,0 +1,409 @@
+#include <math.h>
+#include <stdlib.h>
+#include <clutter/clutter.h>
+
+typedef struct _MultiLayout             MultiLayout;
+typedef struct _MultiLayoutClass        MultiLayoutClass;
+
+typedef enum {
+  MULTI_LAYOUT_GRID,
+  MULTI_LAYOUT_CIRCLE
+} MultiLayoutState;
+
+struct _MultiLayout
+{
+  ClutterLayoutManager parent_instance;
+
+  /* the state of the layout */
+  MultiLayoutState state;
+
+  /* spacing between children */
+  float spacing;
+
+  /* cell size */
+  float cell_width;
+  float cell_height;
+};
+
+struct _MultiLayoutClass
+{
+  ClutterLayoutManagerClass parent_class;
+};
+
+GType multi_layout_get_type (void);
+
+ClutterLayoutManager *  multi_layout_new                (void);
+void                    multi_layout_set_state          (MultiLayout      *layout,
+                                                         MultiLayoutState  state);
+void                    multi_layout_set_spacing        (MultiLayout      *layout,
+                                                         float             spacing);
+
+G_DEFINE_TYPE (MultiLayout, multi_layout, CLUTTER_TYPE_LAYOUT_MANAGER)
+
+static void
+multi_layout_get_preferred_width (ClutterLayoutManager *manager,
+                                  ClutterContainer     *container,
+                                  float                 for_height,
+                                  float                *min_width_p,
+                                  float                *nat_width_p)
+{
+  MultiLayout *self = (MultiLayout *) manager;
+  float minimum, natural;
+  float max_natural_width;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  int n_children;
+
+  minimum = natural = 0.f;
+  max_natural_width = 0.f;
+  n_children = 0;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      float child_minimum, child_natural;
+
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      clutter_actor_get_preferred_width (child, -1.f,
+                                         &child_minimum,
+                                         &child_natural);
+
+      max_natural_width = MAX (max_natural_width, child_natural);
+
+      if (self->state == MULTI_LAYOUT_GRID)
+        {
+          minimum += child_minimum;
+          natural += child_natural;
+        }
+      else if (self->state == MULTI_LAYOUT_CIRCLE)
+        {
+          minimum = MAX (minimum, child_minimum);
+          natural = MAX (natural, child_natural);
+        }
+
+      n_children += 1;
+    }
+
+  self->cell_width = max_natural_width;
+
+  minimum += (self->spacing * (n_children - 1));
+  natural += (self->spacing * (n_children - 1));
+
+  if (min_width_p != NULL)
+    *min_width_p = minimum;
+
+  if (nat_width_p != NULL)
+    *nat_width_p = natural;
+}
+
+static void
+multi_layout_get_preferred_height (ClutterLayoutManager *manager,
+                                   ClutterContainer     *container,
+                                   float                 for_width,
+                                   float                *min_height_p,
+                                   float                *nat_height_p)
+{
+  MultiLayout *self = (MultiLayout *) manager;
+  float minimum, natural;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  int n_children;
+
+  minimum = natural = self->spacing * 2.f;
+  n_children = 0;
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      float child_minimum, child_natural;
+
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      clutter_actor_get_preferred_height (child, -1.f,
+                                          &child_minimum,
+                                          &child_natural);
+
+      minimum = MAX (minimum, child_minimum);
+      natural = MAX (natural, child_natural);
+
+      n_children += 1;
+    }
+
+  self->cell_height = natural;
+
+  minimum += (self->spacing * (n_children - 1));
+  natural += (self->spacing * (n_children - 1));
+
+  if (min_height_p != NULL)
+    *min_height_p = minimum;
+
+  if (nat_height_p != NULL)
+    *nat_height_p = natural;
+}
+
+static int
+get_items_per_row (MultiLayout *self,
+                   float        for_width)
+{
+  int n_columns;
+
+  if (for_width < 0)
+    return 1;
+
+  if (self->cell_width <= 0)
+    return 1;
+
+  n_columns = (int) ((for_width + self->spacing) / (self->cell_width + self->spacing));
+
+  return MAX (n_columns, 1);
+}
+
+static int
+get_visible_children (ClutterActor *actor)
+{
+  ClutterActorIter iter;
+  ClutterActor *child;
+  int n_visible_children = 0;
+
+  clutter_actor_iter_init (&iter, actor);
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      if (CLUTTER_ACTOR_IS_VISIBLE (child))
+        n_visible_children += 1;
+    }
+
+  return n_visible_children;
+}
+
+static void
+multi_layout_allocate (ClutterLayoutManager   *manager,
+                       ClutterContainer       *container,
+                       const ClutterActorBox  *allocation,
+                       ClutterAllocationFlags  flags)
+{
+  MultiLayout *self = (MultiLayout *) manager;
+  float avail_width, avail_height;
+  float x_offset, y_offset;
+  ClutterActorIter iter;
+  ClutterActor *child;
+  float item_x, item_y;
+  int n_items, n_items_per_row, item_index;
+  ClutterPoint center;
+  double radius, theta;
+
+  n_items = get_visible_children (CLUTTER_ACTOR (container));
+  if (n_items == 0)
+    return;
+
+  clutter_actor_box_get_origin (allocation, &x_offset, &y_offset);
+  clutter_actor_box_get_size (allocation, &avail_width, &avail_height);
+
+  /* ensure we have an updated value of cell_width and cell_height */
+  multi_layout_get_preferred_width (manager, container, avail_width, NULL, NULL);
+  multi_layout_get_preferred_height (manager, container, avail_height, NULL, NULL);
+
+  item_index = 0;
+
+  if (self->state == MULTI_LAYOUT_GRID)
+    {
+      n_items_per_row = get_items_per_row (self, avail_width);
+      item_x = x_offset;
+      item_y = y_offset;
+    }
+  else if (self->state == MULTI_LAYOUT_CIRCLE)
+    {
+      center.x = allocation->x2 / 2.f;
+      center.y = allocation->y2 / 2.f;
+      radius = MIN ((avail_width - self->cell_width) / 2.0,
+                    (avail_height - self->cell_height) / 2.0);
+    }
+
+  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
+  while (clutter_actor_iter_next (&iter, &child))
+    {
+      ClutterActorBox child_allocation = CLUTTER_ACTOR_BOX_INIT (0, 0, 0, 0);
+
+      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
+        continue;
+
+      if (self->state == MULTI_LAYOUT_GRID)
+        {
+          if (item_index == n_items_per_row)
+            {
+              item_index = 0;
+              item_x = x_offset;
+              item_y += self->cell_height + self->spacing;
+            }
+
+          child_allocation.x1 = item_x;
+          child_allocation.y1 = item_y;
+          child_allocation.x2 = child_allocation.x1 + self->cell_width;
+          child_allocation.y2 = child_allocation.y1 + self->cell_height;
+
+          item_x += self->cell_width + self->spacing;
+        }
+      else if (self->state == MULTI_LAYOUT_CIRCLE)
+        {
+          theta = 2.0 * G_PI / n_items * item_index;
+          child_allocation.x1 = center.x + radius * sinf (theta) - (self->cell_width / 2.f);
+          child_allocation.y1 = center.y + radius * -cosf (theta) - (self->cell_height / 2.f);
+          child_allocation.x2 = child_allocation.x1 + self->cell_width;
+          child_allocation.y2 = child_allocation.y1 + self->cell_height;
+        }
+
+      clutter_actor_save_easing_state (child);
+      clutter_actor_allocate (child, &child_allocation, flags);
+      clutter_actor_restore_easing_state (child);
+
+      item_index += 1;
+    }
+}
+
+static void
+multi_layout_class_init (MultiLayoutClass *klass)
+{
+  ClutterLayoutManagerClass *manager_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
+
+  manager_class->get_preferred_width = multi_layout_get_preferred_width;
+  manager_class->get_preferred_height = multi_layout_get_preferred_height;
+  manager_class->allocate = multi_layout_allocate;
+}
+
+static void
+multi_layout_init (MultiLayout *self)
+{
+  self->state = MULTI_LAYOUT_GRID;
+
+  self->cell_width = -1.f;
+  self->cell_height = -1.f;
+
+  self->spacing = 0.f;
+}
+
+ClutterLayoutManager *
+multi_layout_new (void)
+{
+  return g_object_new (multi_layout_get_type (), NULL);
+}
+
+void
+multi_layout_set_state (MultiLayout *self,
+                        MultiLayoutState  state)
+{
+  if (self->state == state)
+    return;
+
+  self->state = state;
+
+  clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
+}
+
+void
+multi_layout_set_spacing (MultiLayout *self,
+                          float spacing)
+{
+  self->spacing = spacing;
+
+  clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
+}
+
+#define N_RECTS         16
+#define RECT_SIZE       64.0
+#define N_ROWS          4
+#define PADDING         12.0
+#define BOX_SIZE        (RECT_SIZE * (N_RECTS / N_ROWS) + PADDING * (N_RECTS / N_ROWS - 1))
+
+static gboolean
+on_key_press (ClutterActor *stage,
+              ClutterEvent *event,
+              ClutterActor *box)
+{
+  guint keysym = clutter_event_get_key_symbol (event);
+  MultiLayout *layout = (MultiLayout *) clutter_actor_get_layout_manager (box);
+
+  if (keysym == CLUTTER_KEY_q)
+    {
+      clutter_main_quit ();
+      return CLUTTER_EVENT_STOP;
+    }
+
+  switch (keysym)
+    {
+    case CLUTTER_KEY_g:
+      multi_layout_set_state (layout, MULTI_LAYOUT_GRID);
+      break;
+
+    case CLUTTER_KEY_c:
+      multi_layout_set_state (layout, MULTI_LAYOUT_CIRCLE);
+      break;
+
+    default:
+      break;
+    }
+
+  return CLUTTER_EVENT_STOP;
+}
+
+int
+main (int argc, char *argv[])
+{
+  ClutterActor *stage, *box, *label;
+  ClutterLayoutManager *manager;
+  ClutterMargin margin;
+  int i;
+
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  stage = clutter_stage_new ();
+  clutter_stage_set_title (CLUTTER_STAGE (stage), "Multi-layout");
+  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+  clutter_actor_show (stage);
+
+  manager = multi_layout_new ();
+  multi_layout_set_spacing ((MultiLayout *) manager, PADDING);
+
+  margin.top = margin.bottom = margin.left = margin.right = PADDING;
+
+  box = clutter_actor_new ();
+  clutter_actor_set_margin (box, &margin);
+  clutter_actor_set_layout_manager (box, manager);
+  clutter_actor_set_size (box, BOX_SIZE, BOX_SIZE);
+  clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5));
+  clutter_actor_add_child (stage, box);
+
+  for (i = 0; i < N_RECTS; i++)
+    {
+      ClutterActor *rect = clutter_actor_new ();
+      ClutterColor color;
+
+      clutter_color_from_hls (&color,
+                              360.0 / N_RECTS * i,
+                              0.5,
+                              0.8);
+
+      color.alpha = 128 + 128 / N_RECTS * i;
+
+      clutter_actor_set_size (rect, RECT_SIZE, RECT_SIZE);
+      clutter_actor_set_background_color (rect, &color);
+      clutter_actor_add_child (box, rect);
+    }
+
+  label = clutter_text_new ();
+  clutter_text_set_text (CLUTTER_TEXT (label),
+                         "Press g\t\342\236\236\tGrid layout\n"
+                         "Press c\t\342\236\236\tCircular layout\n"
+                         "Press q\t\342\236\236\tQuit");
+  clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
+  clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.95));
+  clutter_actor_add_child (stage, label);
+
+  g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press), box);
+
+  clutter_main ();
+
+  return EXIT_SUCCESS;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]