examples/layout: Initialize variables to avoid warnings
[profile/ivi/clutter.git] / examples / layout-manager.c
1 #include <math.h>
2 #include <stdlib.h>
3 #include <clutter/clutter.h>
4
5 typedef struct _MultiLayout             MultiLayout;
6 typedef struct _MultiLayoutClass        MultiLayoutClass;
7
8 typedef enum {
9   MULTI_LAYOUT_GRID,
10   MULTI_LAYOUT_CIRCLE
11 } MultiLayoutState;
12
13 struct _MultiLayout
14 {
15   ClutterLayoutManager parent_instance;
16
17   /* the state of the layout */
18   MultiLayoutState state;
19
20   /* spacing between children */
21   float spacing;
22
23   /* cell size */
24   float cell_width;
25   float cell_height;
26 };
27
28 struct _MultiLayoutClass
29 {
30   ClutterLayoutManagerClass parent_class;
31 };
32
33 GType multi_layout_get_type (void);
34
35 ClutterLayoutManager *  multi_layout_new                (void);
36 void                    multi_layout_set_state          (MultiLayout      *layout,
37                                                          MultiLayoutState  state);
38 void                    multi_layout_set_spacing        (MultiLayout      *layout,
39                                                          float             spacing);
40
41 G_DEFINE_TYPE (MultiLayout, multi_layout, CLUTTER_TYPE_LAYOUT_MANAGER)
42
43 static void
44 multi_layout_get_preferred_width (ClutterLayoutManager *manager,
45                                   ClutterContainer     *container,
46                                   float                 for_height,
47                                   float                *min_width_p,
48                                   float                *nat_width_p)
49 {
50   MultiLayout *self = (MultiLayout *) manager;
51   float minimum, natural;
52   float max_natural_width;
53   ClutterActorIter iter;
54   ClutterActor *child;
55   int n_children;
56
57   minimum = natural = 0.f;
58   max_natural_width = 0.f;
59   n_children = 0;
60
61   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
62   while (clutter_actor_iter_next (&iter, &child))
63     {
64       float child_minimum, child_natural;
65
66       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
67         continue;
68
69       clutter_actor_get_preferred_width (child, -1.f,
70                                          &child_minimum,
71                                          &child_natural);
72
73       max_natural_width = MAX (max_natural_width, child_natural);
74
75       if (self->state == MULTI_LAYOUT_GRID)
76         {
77           minimum += child_minimum;
78           natural += child_natural;
79         }
80       else if (self->state == MULTI_LAYOUT_CIRCLE)
81         {
82           minimum = MAX (minimum, child_minimum);
83           natural = MAX (natural, child_natural);
84         }
85
86       n_children += 1;
87     }
88
89   self->cell_width = max_natural_width;
90
91   minimum += (self->spacing * (n_children - 1));
92   natural += (self->spacing * (n_children - 1));
93
94   if (min_width_p != NULL)
95     *min_width_p = minimum;
96
97   if (nat_width_p != NULL)
98     *nat_width_p = natural;
99 }
100
101 static void
102 multi_layout_get_preferred_height (ClutterLayoutManager *manager,
103                                    ClutterContainer     *container,
104                                    float                 for_width,
105                                    float                *min_height_p,
106                                    float                *nat_height_p)
107 {
108   MultiLayout *self = (MultiLayout *) manager;
109   float minimum, natural;
110   ClutterActorIter iter;
111   ClutterActor *child;
112   int n_children;
113
114   minimum = natural = self->spacing * 2.f;
115   n_children = 0;
116
117   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
118   while (clutter_actor_iter_next (&iter, &child))
119     {
120       float child_minimum, child_natural;
121
122       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
123         continue;
124
125       clutter_actor_get_preferred_height (child, -1.f,
126                                           &child_minimum,
127                                           &child_natural);
128
129       minimum = MAX (minimum, child_minimum);
130       natural = MAX (natural, child_natural);
131
132       n_children += 1;
133     }
134
135   self->cell_height = natural;
136
137   minimum += (self->spacing * (n_children - 1));
138   natural += (self->spacing * (n_children - 1));
139
140   if (min_height_p != NULL)
141     *min_height_p = minimum;
142
143   if (nat_height_p != NULL)
144     *nat_height_p = natural;
145 }
146
147 static int
148 get_items_per_row (MultiLayout *self,
149                    float        for_width)
150 {
151   int n_columns;
152
153   if (for_width < 0)
154     return 1;
155
156   if (self->cell_width <= 0)
157     return 1;
158
159   n_columns = (int) ((for_width + self->spacing) / (self->cell_width + self->spacing));
160
161   return MAX (n_columns, 1);
162 }
163
164 static int
165 get_visible_children (ClutterActor *actor)
166 {
167   ClutterActorIter iter;
168   ClutterActor *child;
169   int n_visible_children = 0;
170
171   clutter_actor_iter_init (&iter, actor);
172   while (clutter_actor_iter_next (&iter, &child))
173     {
174       if (CLUTTER_ACTOR_IS_VISIBLE (child))
175         n_visible_children += 1;
176     }
177
178   return n_visible_children;
179 }
180
181 static void
182 multi_layout_allocate (ClutterLayoutManager   *manager,
183                        ClutterContainer       *container,
184                        const ClutterActorBox  *allocation,
185                        ClutterAllocationFlags  flags)
186 {
187   MultiLayout *self = (MultiLayout *) manager;
188   float avail_width, avail_height;
189   float x_offset, y_offset;
190   ClutterActorIter iter;
191   ClutterActor *child;
192   float item_x = 0.f, item_y = 0.f;
193   int n_items, n_items_per_row = 0, item_index;
194   ClutterPoint center = CLUTTER_POINT_INIT_ZERO;
195   double radius = 0, theta = 0;
196   gboolean use_animations;
197   ClutterAnimationMode easing_mode;
198   guint easing_duration, easing_delay;
199
200   n_items = get_visible_children (CLUTTER_ACTOR (container));
201   if (n_items == 0)
202     return;
203
204   clutter_actor_box_get_origin (allocation, &x_offset, &y_offset);
205   clutter_actor_box_get_size (allocation, &avail_width, &avail_height);
206
207   /* ensure we have an updated value of cell_width and cell_height */
208   multi_layout_get_preferred_width (manager, container, avail_width, NULL, NULL);
209   multi_layout_get_preferred_height (manager, container, avail_height, NULL, NULL);
210
211   item_index = 0;
212
213   if (self->state == MULTI_LAYOUT_GRID)
214     {
215       n_items_per_row = get_items_per_row (self, avail_width);
216       item_x = x_offset;
217       item_y = y_offset;
218     }
219   else if (self->state == MULTI_LAYOUT_CIRCLE)
220     {
221       center.x = allocation->x2 / 2.f;
222       center.y = allocation->y2 / 2.f;
223       radius = MIN ((avail_width - self->cell_width) / 2.0,
224                     (avail_height - self->cell_height) / 2.0);
225     }
226
227   use_animations = clutter_layout_manager_get_easing_state (manager,
228                                                             &easing_mode,
229                                                             &easing_duration,
230                                                             &easing_delay);
231
232   clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
233   while (clutter_actor_iter_next (&iter, &child))
234     {
235       ClutterActorBox child_allocation = CLUTTER_ACTOR_BOX_INIT (0, 0, 0, 0);
236
237       if (!CLUTTER_ACTOR_IS_VISIBLE (child))
238         continue;
239
240       if (self->state == MULTI_LAYOUT_GRID)
241         {
242           if (item_index == n_items_per_row)
243             {
244               item_index = 0;
245               item_x = x_offset;
246               item_y += self->cell_height + self->spacing;
247             }
248
249           child_allocation.x1 = item_x;
250           child_allocation.y1 = item_y;
251           child_allocation.x2 = child_allocation.x1 + self->cell_width;
252           child_allocation.y2 = child_allocation.y1 + self->cell_height;
253
254           item_x += self->cell_width + self->spacing;
255         }
256       else if (self->state == MULTI_LAYOUT_CIRCLE)
257         {
258           theta = 2.0 * G_PI / n_items * item_index;
259           child_allocation.x1 = center.x + radius * sinf (theta) - (self->cell_width / 2.f);
260           child_allocation.y1 = center.y + radius * -cosf (theta) - (self->cell_height / 2.f);
261           child_allocation.x2 = child_allocation.x1 + self->cell_width;
262           child_allocation.y2 = child_allocation.y1 + self->cell_height;
263         }
264
265       if (use_animations)
266         {
267           clutter_actor_save_easing_state (child);
268           clutter_actor_set_easing_mode (child, easing_mode);
269           clutter_actor_set_easing_duration (child, easing_duration);
270           clutter_actor_set_easing_delay (child, easing_delay);
271         }
272
273       clutter_actor_allocate (child, &child_allocation, flags);
274
275       if (use_animations)
276         clutter_actor_restore_easing_state (child);
277
278       item_index += 1;
279     }
280 }
281
282 static void
283 multi_layout_class_init (MultiLayoutClass *klass)
284 {
285   ClutterLayoutManagerClass *manager_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
286
287   manager_class->get_preferred_width = multi_layout_get_preferred_width;
288   manager_class->get_preferred_height = multi_layout_get_preferred_height;
289   manager_class->allocate = multi_layout_allocate;
290 }
291
292 static void
293 multi_layout_init (MultiLayout *self)
294 {
295   self->state = MULTI_LAYOUT_GRID;
296
297   self->cell_width = -1.f;
298   self->cell_height = -1.f;
299
300   self->spacing = 0.f;
301 }
302
303 ClutterLayoutManager *
304 multi_layout_new (void)
305 {
306   return g_object_new (multi_layout_get_type (), NULL);
307 }
308
309 void
310 multi_layout_set_state (MultiLayout *self,
311                         MultiLayoutState  state)
312 {
313   if (self->state == state)
314     return;
315
316   self->state = state;
317
318   clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
319 }
320
321 void
322 multi_layout_set_spacing (MultiLayout *self,
323                           float spacing)
324 {
325   self->spacing = spacing;
326
327   clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
328 }
329
330 #define N_RECTS         16
331 #define RECT_SIZE       64.0
332 #define N_ROWS          4
333 #define PADDING         12.0
334 #define BOX_SIZE        (RECT_SIZE * (N_RECTS / N_ROWS) + PADDING * (N_RECTS / N_ROWS - 1))
335
336 static gboolean
337 on_key_press (ClutterActor *stage,
338               ClutterEvent *event,
339               ClutterActor *box)
340 {
341   guint keysym = clutter_event_get_key_symbol (event);
342   MultiLayout *layout = (MultiLayout *) clutter_actor_get_layout_manager (box);
343
344   if (keysym == CLUTTER_KEY_q)
345     {
346       clutter_main_quit ();
347       return CLUTTER_EVENT_STOP;
348     }
349
350   switch (keysym)
351     {
352     case CLUTTER_KEY_g:
353       multi_layout_set_state (layout, MULTI_LAYOUT_GRID);
354       break;
355
356     case CLUTTER_KEY_c:
357       multi_layout_set_state (layout, MULTI_LAYOUT_CIRCLE);
358       break;
359
360     default:
361       break;
362     }
363
364   return CLUTTER_EVENT_STOP;
365 }
366
367 int
368 main (int argc, char *argv[])
369 {
370   ClutterActor *stage, *box, *label;
371   ClutterLayoutManager *manager;
372   ClutterMargin margin;
373   int i;
374
375   if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
376     return EXIT_FAILURE;
377
378   stage = clutter_stage_new ();
379   clutter_stage_set_title (CLUTTER_STAGE (stage), "Multi-layout");
380   g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
381   clutter_actor_show (stage);
382
383   manager = multi_layout_new ();
384   multi_layout_set_spacing ((MultiLayout *) manager, PADDING);
385   clutter_layout_manager_set_use_animations (manager, TRUE);
386
387   margin.top = margin.bottom = margin.left = margin.right = PADDING;
388
389   box = clutter_actor_new ();
390   clutter_actor_set_margin (box, &margin);
391   clutter_actor_set_layout_manager (box, manager);
392   clutter_actor_set_size (box, BOX_SIZE, BOX_SIZE);
393   clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5));
394   clutter_actor_add_child (stage, box);
395
396   for (i = 0; i < N_RECTS; i++)
397     {
398       ClutterActor *rect = clutter_actor_new ();
399       ClutterColor color;
400
401       clutter_color_from_hls (&color,
402                               360.0 / N_RECTS * i,
403                               0.5,
404                               0.8);
405
406       color.alpha = 128 + 128 / N_RECTS * i;
407
408       clutter_actor_set_size (rect, RECT_SIZE, RECT_SIZE);
409       clutter_actor_set_background_color (rect, &color);
410       clutter_actor_add_child (box, rect);
411     }
412
413   label = clutter_text_new ();
414   clutter_text_set_text (CLUTTER_TEXT (label),
415                          "Press g\t\342\236\236\tGrid layout\n"
416                          "Press c\t\342\236\236\tCircular layout\n"
417                          "Press q\t\342\236\236\tQuit");
418   clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
419   clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.95));
420   clutter_actor_add_child (stage, label);
421
422   g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press), box);
423
424   clutter_main ();
425
426   return EXIT_SUCCESS;
427 }