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 MultiLayoutState multi_layout_get_state (MultiLayout *layout);
39 void multi_layout_set_spacing (MultiLayout *layout,
40 float spacing);
41
G_DEFINE_TYPE(MultiLayout,multi_layout,CLUTTER_TYPE_LAYOUT_MANAGER)42 G_DEFINE_TYPE (MultiLayout, multi_layout, CLUTTER_TYPE_LAYOUT_MANAGER)
43
44 static void
45 multi_layout_get_preferred_width (ClutterLayoutManager *manager,
46 ClutterContainer *container,
47 float for_height,
48 float *min_width_p,
49 float *nat_width_p)
50 {
51 MultiLayout *self = (MultiLayout *) manager;
52 float minimum, natural;
53 float max_natural_width;
54 ClutterActorIter iter;
55 ClutterActor *child;
56 int n_children;
57
58 minimum = natural = 0.f;
59 max_natural_width = 0.f;
60 n_children = 0;
61
62 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
63 while (clutter_actor_iter_next (&iter, &child))
64 {
65 float child_minimum, child_natural;
66
67 if (!clutter_actor_is_visible (child))
68 continue;
69
70 clutter_actor_get_preferred_width (child, -1.f,
71 &child_minimum,
72 &child_natural);
73
74 max_natural_width = MAX (max_natural_width, child_natural);
75
76 if (self->state == MULTI_LAYOUT_GRID)
77 {
78 minimum += child_minimum;
79 natural += child_natural;
80 }
81 else if (self->state == MULTI_LAYOUT_CIRCLE)
82 {
83 minimum = MAX (minimum, child_minimum);
84 natural = MAX (natural, child_natural);
85 }
86
87 n_children += 1;
88 }
89
90 self->cell_width = max_natural_width;
91
92 minimum += (self->spacing * (n_children - 1));
93 natural += (self->spacing * (n_children - 1));
94
95 if (min_width_p != NULL)
96 *min_width_p = minimum;
97
98 if (nat_width_p != NULL)
99 *nat_width_p = natural;
100 }
101
102 static void
multi_layout_get_preferred_height(ClutterLayoutManager * manager,ClutterContainer * container,float for_width,float * min_height_p,float * nat_height_p)103 multi_layout_get_preferred_height (ClutterLayoutManager *manager,
104 ClutterContainer *container,
105 float for_width,
106 float *min_height_p,
107 float *nat_height_p)
108 {
109 MultiLayout *self = (MultiLayout *) manager;
110 float minimum, natural;
111 ClutterActorIter iter;
112 ClutterActor *child;
113 int n_children;
114
115 minimum = natural = self->spacing * 2.f;
116 n_children = 0;
117
118 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
119 while (clutter_actor_iter_next (&iter, &child))
120 {
121 float child_minimum, child_natural;
122
123 if (!clutter_actor_is_visible (child))
124 continue;
125
126 clutter_actor_get_preferred_height (child, -1.f,
127 &child_minimum,
128 &child_natural);
129
130 minimum = MAX (minimum, child_minimum);
131 natural = MAX (natural, child_natural);
132
133 n_children += 1;
134 }
135
136 self->cell_height = natural;
137
138 minimum += (self->spacing * (n_children - 1));
139 natural += (self->spacing * (n_children - 1));
140
141 if (min_height_p != NULL)
142 *min_height_p = minimum;
143
144 if (nat_height_p != NULL)
145 *nat_height_p = natural;
146 }
147
148 static int
get_items_per_row(MultiLayout * self,float for_width)149 get_items_per_row (MultiLayout *self,
150 float for_width)
151 {
152 int n_columns;
153
154 if (for_width < 0)
155 return 1;
156
157 if (self->cell_width <= 0)
158 return 1;
159
160 n_columns = (int) ((for_width + self->spacing) / (self->cell_width + self->spacing));
161
162 return MAX (n_columns, 1);
163 }
164
165 static int
get_visible_children(ClutterActor * actor)166 get_visible_children (ClutterActor *actor)
167 {
168 ClutterActorIter iter;
169 ClutterActor *child;
170 int n_visible_children = 0;
171
172 clutter_actor_iter_init (&iter, actor);
173 while (clutter_actor_iter_next (&iter, &child))
174 {
175 if (clutter_actor_is_visible (child))
176 n_visible_children += 1;
177 }
178
179 return n_visible_children;
180 }
181
182 static void
multi_layout_allocate(ClutterLayoutManager * manager,ClutterContainer * container,const ClutterActorBox * allocation,ClutterAllocationFlags flags)183 multi_layout_allocate (ClutterLayoutManager *manager,
184 ClutterContainer *container,
185 const ClutterActorBox *allocation,
186 ClutterAllocationFlags flags)
187 {
188 MultiLayout *self = (MultiLayout *) manager;
189 float avail_width, avail_height;
190 float x_offset, y_offset;
191 ClutterActorIter iter;
192 ClutterActor *child;
193 float item_x = 0.f, item_y = 0.f;
194 int n_items, n_items_per_row = 0, item_index;
195 ClutterPoint center = CLUTTER_POINT_INIT_ZERO;
196 double radius = 0, theta = 0;
197
198 n_items = get_visible_children (CLUTTER_ACTOR (container));
199 if (n_items == 0)
200 return;
201
202 clutter_actor_box_get_origin (allocation, &x_offset, &y_offset);
203 clutter_actor_box_get_size (allocation, &avail_width, &avail_height);
204
205 /* ensure we have an updated value of cell_width and cell_height */
206 multi_layout_get_preferred_width (manager, container, avail_width, NULL, NULL);
207 multi_layout_get_preferred_height (manager, container, avail_height, NULL, NULL);
208
209 item_index = 0;
210
211 if (self->state == MULTI_LAYOUT_GRID)
212 {
213 n_items_per_row = get_items_per_row (self, avail_width);
214 item_x = x_offset;
215 item_y = y_offset;
216 }
217 else if (self->state == MULTI_LAYOUT_CIRCLE)
218 {
219 center.x = allocation->x2 / 2.f;
220 center.y = allocation->y2 / 2.f;
221 radius = MIN ((avail_width - self->cell_width) / 2.0,
222 (avail_height - self->cell_height) / 2.0);
223 }
224
225 clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container));
226 while (clutter_actor_iter_next (&iter, &child))
227 {
228 ClutterActorBox child_allocation = CLUTTER_ACTOR_BOX_INIT_ZERO;
229
230 if (!clutter_actor_is_visible (child))
231 continue;
232
233 if (self->state == MULTI_LAYOUT_GRID)
234 {
235 if (item_index == n_items_per_row)
236 {
237 item_index = 0;
238 item_x = x_offset;
239 item_y += self->cell_height + self->spacing;
240 }
241
242 child_allocation.x1 = item_x;
243 child_allocation.y1 = item_y;
244 child_allocation.x2 = child_allocation.x1 + self->cell_width;
245 child_allocation.y2 = child_allocation.y1 + self->cell_height;
246
247 item_x += self->cell_width + self->spacing;
248 }
249 else if (self->state == MULTI_LAYOUT_CIRCLE)
250 {
251 theta = 2.0 * G_PI / n_items * item_index;
252 child_allocation.x1 = center.x + radius * sinf (theta) - (self->cell_width / 2.f);
253 child_allocation.y1 = center.y + radius * -cosf (theta) - (self->cell_height / 2.f);
254 child_allocation.x2 = child_allocation.x1 + self->cell_width;
255 child_allocation.y2 = child_allocation.y1 + self->cell_height;
256 }
257
258 clutter_actor_allocate (child, &child_allocation, flags);
259
260 item_index += 1;
261 }
262 }
263
264 static void
multi_layout_class_init(MultiLayoutClass * klass)265 multi_layout_class_init (MultiLayoutClass *klass)
266 {
267 ClutterLayoutManagerClass *manager_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass);
268
269 manager_class->get_preferred_width = multi_layout_get_preferred_width;
270 manager_class->get_preferred_height = multi_layout_get_preferred_height;
271 manager_class->allocate = multi_layout_allocate;
272 }
273
274 static void
multi_layout_init(MultiLayout * self)275 multi_layout_init (MultiLayout *self)
276 {
277 self->state = MULTI_LAYOUT_GRID;
278
279 self->cell_width = -1.f;
280 self->cell_height = -1.f;
281
282 self->spacing = 0.f;
283 }
284
285 ClutterLayoutManager *
multi_layout_new(void)286 multi_layout_new (void)
287 {
288 return g_object_new (multi_layout_get_type (), NULL);
289 }
290
291 void
multi_layout_set_state(MultiLayout * self,MultiLayoutState state)292 multi_layout_set_state (MultiLayout *self,
293 MultiLayoutState state)
294 {
295 if (self->state == state)
296 return;
297
298 self->state = state;
299
300 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
301 }
302
303 MultiLayoutState
multi_layout_get_state(MultiLayout * self)304 multi_layout_get_state (MultiLayout *self)
305 {
306 return self->state;
307 }
308
309 void
multi_layout_set_spacing(MultiLayout * self,float spacing)310 multi_layout_set_spacing (MultiLayout *self,
311 float spacing)
312 {
313 self->spacing = spacing;
314
315 clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (self));
316 }
317
318 #define N_RECTS 16
319 #define RECT_SIZE 64.0
320 #define N_ROWS 4
321 #define PADDING 12.0
322 #define BOX_SIZE (RECT_SIZE * (N_RECTS / N_ROWS) + PADDING * (N_RECTS / N_ROWS - 1))
323
324 static gboolean
on_enter(ClutterActor * rect,ClutterEvent * event)325 on_enter (ClutterActor *rect,
326 ClutterEvent *event)
327 {
328 clutter_actor_set_scale (rect, 1.2, 1.2);
329
330 return CLUTTER_EVENT_STOP;
331 }
332
333 static gboolean
on_leave(ClutterActor * rect,ClutterEvent * event)334 on_leave (ClutterActor *rect,
335 ClutterEvent *event)
336 {
337 clutter_actor_set_scale (rect, 1.0, 1.0);
338
339 return CLUTTER_EVENT_STOP;
340 }
341
342 static gboolean
on_key_press(ClutterActor * stage,ClutterEvent * event,ClutterActor * box)343 on_key_press (ClutterActor *stage,
344 ClutterEvent *event,
345 ClutterActor *box)
346 {
347 guint keysym = clutter_event_get_key_symbol (event);
348 MultiLayout *layout = (MultiLayout *) clutter_actor_get_layout_manager (box);
349
350
351 switch (keysym)
352 {
353 case CLUTTER_KEY_q:
354 clutter_main_quit ();
355 break;
356
357 case CLUTTER_KEY_t:
358 {
359 MultiLayoutState state = multi_layout_get_state (layout);
360
361 if (state == MULTI_LAYOUT_GRID)
362 multi_layout_set_state (layout, MULTI_LAYOUT_CIRCLE);
363
364 if (state == MULTI_LAYOUT_CIRCLE)
365 multi_layout_set_state (layout, MULTI_LAYOUT_GRID);
366 }
367 break;
368
369 default:
370 break;
371 }
372
373 return CLUTTER_EVENT_STOP;
374 }
375
376 int
main(int argc,char * argv[])377 main (int argc, char *argv[])
378 {
379 ClutterActor *stage, *box, *label;
380 ClutterLayoutManager *manager;
381 ClutterMargin margin;
382 ClutterTransition *transition;
383 int i;
384
385 if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
386 return EXIT_FAILURE;
387
388 stage = clutter_stage_new ();
389 clutter_stage_set_title (CLUTTER_STAGE (stage), "Multi-layout");
390 g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
391 clutter_actor_show (stage);
392
393 /* the layout manager for the main container */
394 manager = multi_layout_new ();
395 multi_layout_set_spacing ((MultiLayout *) manager, PADDING);
396
397 margin.top = margin.bottom = margin.left = margin.right = PADDING;
398
399 /* our main container, centered on the stage */
400 box = clutter_actor_new ();
401 clutter_actor_set_margin (box, &margin);
402 clutter_actor_set_layout_manager (box, manager);
403 clutter_actor_set_size (box, BOX_SIZE, BOX_SIZE);
404 clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5));
405 clutter_actor_add_child (stage, box);
406
407 for (i = 0; i < N_RECTS; i++)
408 {
409 ClutterActor *rect = clutter_actor_new ();
410 ClutterColor color;
411
412 clutter_color_from_hls (&color,
413 360.0 / N_RECTS * i,
414 0.5,
415 0.8);
416
417 color.alpha = 128 + 128 / N_RECTS * i;
418
419 /* elements on the layout */
420 clutter_actor_set_size (rect, RECT_SIZE, RECT_SIZE);
421 clutter_actor_set_pivot_point (rect, .5f, .5f);
422 clutter_actor_set_background_color (rect, &color);
423 clutter_actor_set_opacity (rect, 0);
424 clutter_actor_set_reactive (rect, TRUE);
425
426 /* explicit transition that fades in the element; the delay on
427 * the transition staggers the fade depending on the index
428 */
429 transition = clutter_property_transition_new ("opacity");
430 clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 250);
431 clutter_timeline_set_delay (CLUTTER_TIMELINE (transition), i * 50);
432 clutter_transition_set_from (transition, G_TYPE_UINT, 0);
433 clutter_transition_set_to (transition, G_TYPE_UINT, 255);
434 clutter_actor_add_transition (rect, "fadeIn", transition);
435 g_object_unref (transition);
436
437 /* we want all state transitions to be animated */
438 clutter_actor_set_easing_duration (rect, 250);
439 clutter_actor_set_easing_mode (rect, CLUTTER_EASE_OUT_CUBIC);
440
441 clutter_actor_add_child (box, rect);
442
443 /* simple hover effect */
444 g_signal_connect (rect, "enter-event", G_CALLBACK (on_enter), NULL);
445 g_signal_connect (rect, "leave-event", G_CALLBACK (on_leave), NULL);
446 }
447
448 label = clutter_text_new ();
449 clutter_text_set_text (CLUTTER_TEXT (label),
450 "Press t\t\342\236\236\tToggle layout\n"
451 "Press q\t\342\236\236\tQuit");
452 clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
453 clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.95));
454 clutter_actor_add_child (stage, label);
455
456 g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press), box);
457
458 clutter_main ();
459
460 return EXIT_SUCCESS;
461 }
462