From: Emmanuele Bassi Date: Fri, 4 May 2012 16:43:30 +0000 (+0100) Subject: examples: Add an example of layout manager X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4d087f2c0ada4c7f5f6a9bd1aa86b337c05e9f1b;p=profile%2Fivi%2Fclutter.git 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. --- 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 +#include +#include + +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; +}