No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-bin.c: Basic container actor
4 *
5 * Copyright 2009 Intel Corporation.
6 * Copyright 2009, 2010 Red Hat, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU Lesser General Public License,
10 * version 2.1, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /**
22 * SECTION:st-bin
23 * @short_description: a simple container with one actor
24 *
25 * #StBin is a simple container capable of having only one
26 * #ClutterActor as a child.
27 *
28 * #StBin inherits from #StWidget, so it is fully themable.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <clutter/clutter.h>
36
37 #include "st-bin.h"
38 #include "st-enum-types.h"
39 #include "st-private.h"
40
41 #define ST_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BIN, StBinPrivate))
42
43 struct _StBinPrivate
44 {
45 ClutterActor *child;
46
47 StAlign x_align;
48 StAlign y_align;
49
50 guint x_fill : 1;
51 guint y_fill : 1;
52 };
53
54 enum
55 {
56 PROP_0,
57
58 PROP_CHILD,
59 PROP_X_ALIGN,
60 PROP_Y_ALIGN,
61 PROP_X_FILL,
62 PROP_Y_FILL
63 };
64
65 static void clutter_container_iface_init (ClutterContainerIface *iface);
66
67 G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET,
68 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
69 clutter_container_iface_init));
70
71 static void
72 st_bin_add (ClutterContainer *container,
73 ClutterActor *actor)
74 {
75 st_bin_set_child (ST_BIN (container), actor);
76 }
77
78 static void
79 st_bin_remove (ClutterContainer *container,
80 ClutterActor *actor)
81 {
82 StBinPrivate *priv = ST_BIN (container)->priv;
83
84 if (priv->child == actor)
85 st_bin_set_child (ST_BIN (container), NULL);
86 }
87
88 static void
89 clutter_container_iface_init (ClutterContainerIface *iface)
90 {
91 iface->add = st_bin_add;
92 iface->remove = st_bin_remove;
93 }
94
95 static void
96 st_bin_allocate (ClutterActor *self,
97 const ClutterActorBox *box,
98 ClutterAllocationFlags flags)
99 {
100 StBinPrivate *priv = ST_BIN (self)->priv;
101
102 clutter_actor_set_allocation (self, box, flags);
103
104 if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
105 {
106 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
107 ClutterActorBox childbox;
108 gdouble x_align_f, y_align_f;
109
110 st_theme_node_get_content_box (theme_node, box, &childbox);
111 _st_get_align_factors (priv->x_align, priv->y_align,
112 &x_align_f, &y_align_f);
113 clutter_actor_allocate_align_fill (priv->child, &childbox,
114 x_align_f, y_align_f,
115 priv->x_fill, priv->y_fill,
116 flags);
117 }
118 }
119
120 static void
121 st_bin_get_preferred_width (ClutterActor *self,
122 gfloat for_height,
123 gfloat *min_width_p,
124 gfloat *natural_width_p)
125 {
126 StBinPrivate *priv = ST_BIN (self)->priv;
127 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
128
129 st_theme_node_adjust_for_height (theme_node, &for_height);
130
131 if (priv->child == NULL || !CLUTTER_ACTOR_IS_VISIBLE (priv->child))
132 {
133 if (min_width_p)
134 *min_width_p = 0;
135
136 if (natural_width_p)
137 *natural_width_p = 0;
138 }
139 else
140 {
141 _st_actor_get_preferred_width (priv->child, for_height, priv->y_fill,
142 min_width_p,
143 natural_width_p);
144 }
145
146 st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
147 }
148
149 static void
150 st_bin_get_preferred_height (ClutterActor *self,
151 gfloat for_width,
152 gfloat *min_height_p,
153 gfloat *natural_height_p)
154 {
155 StBinPrivate *priv = ST_BIN (self)->priv;
156 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
157
158 st_theme_node_adjust_for_width (theme_node, &for_width);
159
160 if (priv->child == NULL || !CLUTTER_ACTOR_IS_VISIBLE (priv->child))
161 {
162 if (min_height_p)
163 *min_height_p = 0;
164
165 if (natural_height_p)
166 *natural_height_p = 0;
167 }
168 else
169 {
170 _st_actor_get_preferred_height (priv->child, for_width, priv->x_fill,
171 min_height_p,
172 natural_height_p);
173 }
174
175 st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
176 }
177
178 static void
179 st_bin_dispose (GObject *gobject)
180 {
181 StBinPrivate *priv = ST_BIN (gobject)->priv;
182
183 if (priv->child)
184 clutter_actor_destroy (priv->child);
185 g_assert (priv->child == NULL);
186
187 G_OBJECT_CLASS (st_bin_parent_class)->dispose (gobject);
188 }
189
190 static gboolean
191 st_bin_navigate_focus (StWidget *widget,
192 ClutterActor *from,
193 GtkDirectionType direction)
194 {
195 StBinPrivate *priv = ST_BIN (widget)->priv;
196 ClutterActor *bin_actor = CLUTTER_ACTOR (widget);
197
198 if (st_widget_get_can_focus (widget))
199 {
200 if (from && clutter_actor_contains (bin_actor, from))
201 return FALSE;
202
203 if (CLUTTER_ACTOR_IS_MAPPED (bin_actor))
204 {
205 clutter_actor_grab_key_focus (bin_actor);
206 return TRUE;
207 }
208 else
209 {
210 return FALSE;
211 }
212 }
213 else if (priv->child && ST_IS_WIDGET (priv->child))
214 return st_widget_navigate_focus (ST_WIDGET (priv->child), from, direction, FALSE);
215 else
216 return FALSE;
217 }
218
219 static void
220 st_bin_set_property (GObject *gobject,
221 guint prop_id,
222 const GValue *value,
223 GParamSpec *pspec)
224 {
225 StBin *bin = ST_BIN (gobject);
226
227 switch (prop_id)
228 {
229 case PROP_CHILD:
230 st_bin_set_child (bin, g_value_get_object (value));
231 break;
232
233 case PROP_X_ALIGN:
234 st_bin_set_alignment (bin,
235 g_value_get_enum (value),
236 bin->priv->y_align);
237 break;
238
239 case PROP_Y_ALIGN:
240 st_bin_set_alignment (bin,
241 bin->priv->x_align,
242 g_value_get_enum (value));
243 break;
244
245 case PROP_X_FILL:
246 st_bin_set_fill (bin,
247 g_value_get_boolean (value),
248 bin->priv->y_fill);
249 break;
250
251 case PROP_Y_FILL:
252 st_bin_set_fill (bin,
253 bin->priv->y_fill,
254 g_value_get_boolean (value));
255 break;
256
257 default:
258 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
259 }
260 }
261
262 static void
263 st_bin_get_property (GObject *gobject,
264 guint prop_id,
265 GValue *value,
266 GParamSpec *pspec)
267 {
268 StBinPrivate *priv = ST_BIN (gobject)->priv;
269
270 switch (prop_id)
271 {
272 case PROP_CHILD:
273 g_value_set_object (value, priv->child);
274 break;
275
276 case PROP_X_FILL:
277 g_value_set_boolean (value, priv->x_fill);
278 break;
279
280 case PROP_Y_FILL:
281 g_value_set_boolean (value, priv->y_fill);
282 break;
283
284 case PROP_X_ALIGN:
285 g_value_set_enum (value, priv->x_align);
286 break;
287
288 case PROP_Y_ALIGN:
289 g_value_set_enum (value, priv->y_align);
290 break;
291
292 default:
293 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
294 }
295 }
296
297 static void
298 st_bin_class_init (StBinClass *klass)
299 {
300 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
301 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
302 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
303 GParamSpec *pspec;
304
305 g_type_class_add_private (klass, sizeof (StBinPrivate));
306
307 gobject_class->set_property = st_bin_set_property;
308 gobject_class->get_property = st_bin_get_property;
309 gobject_class->dispose = st_bin_dispose;
310
311 actor_class->get_preferred_width = st_bin_get_preferred_width;
312 actor_class->get_preferred_height = st_bin_get_preferred_height;
313 actor_class->allocate = st_bin_allocate;
314
315 widget_class->navigate_focus = st_bin_navigate_focus;
316
317 /**
318 * StBin:child:
319 *
320 * The child #ClutterActor of the #StBin container.
321 */
322 pspec = g_param_spec_object ("child",
323 "Child",
324 "The child of the Bin",
325 CLUTTER_TYPE_ACTOR,
326 ST_PARAM_READWRITE);
327 g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
328
329 /**
330 * StBin:x-align:
331 *
332 * The horizontal alignment of the #StBin child.
333 */
334 pspec = g_param_spec_enum ("x-align",
335 "X Align",
336 "The horizontal alignment",
337 ST_TYPE_ALIGN,
338 ST_ALIGN_MIDDLE,
339 ST_PARAM_READWRITE);
340 g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
341
342 /**
343 * StBin:y-align:
344 *
345 * The vertical alignment of the #StBin child.
346 */
347 pspec = g_param_spec_enum ("y-align",
348 "Y Align",
349 "The vertical alignment",
350 ST_TYPE_ALIGN,
351 ST_ALIGN_MIDDLE,
352 ST_PARAM_READWRITE);
353 g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
354
355 /**
356 * StBin:x-fill:
357 *
358 * Whether the child should fill the horizontal allocation
359 */
360 pspec = g_param_spec_boolean ("x-fill",
361 "X Fill",
362 "Whether the child should fill the "
363 "horizontal allocation",
364 FALSE,
365 ST_PARAM_READWRITE);
366 g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
367
368 /**
369 * StBin:y-fill:
370 *
371 * Whether the child should fill the vertical allocation
372 */
373 pspec = g_param_spec_boolean ("y-fill",
374 "Y Fill",
375 "Whether the child should fill the "
376 "vertical allocation",
377 FALSE,
378 ST_PARAM_READWRITE);
379 g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
380 }
381
382 static void
383 st_bin_init (StBin *bin)
384 {
385 bin->priv = ST_BIN_GET_PRIVATE (bin);
386
387 bin->priv->x_align = ST_ALIGN_MIDDLE;
388 bin->priv->y_align = ST_ALIGN_MIDDLE;
389 }
390
391 /**
392 * st_bin_new:
393 *
394 * Creates a new #StBin, a simple container for one child.
395 *
396 * Return value: the newly created #StBin actor
397 */
398 StWidget *
399 st_bin_new (void)
400 {
401 return g_object_new (ST_TYPE_BIN, NULL);
402 }
403
404 /**
405 * st_bin_set_child:
406 * @bin: a #StBin
407 * @child: (allow-none): a #ClutterActor, or %NULL
408 *
409 * Sets @child as the child of @bin.
410 *
411 * If @bin already has a child, the previous child is removed.
412 */
413 void
414 st_bin_set_child (StBin *bin,
415 ClutterActor *child)
416 {
417 StBinPrivate *priv;
418
419 g_return_if_fail (ST_IS_BIN (bin));
420 g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
421
422 priv = bin->priv;
423
424 if (priv->child == child)
425 return;
426
427 if (priv->child)
428 clutter_actor_remove_child (CLUTTER_ACTOR (bin), priv->child);
429
430 priv->child = NULL;
431
432 if (child)
433 {
434 priv->child = child;
435 clutter_actor_add_child (CLUTTER_ACTOR (bin), child);
436 }
437
438 clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
439
440 g_object_notify (G_OBJECT (bin), "child");
441 }
442
443 /**
444 * st_bin_get_child:
445 * @bin: a #StBin
446 *
447 * Retrieves a pointer to the child of @bin.
448 *
449 * Return value: (transfer none): a #ClutterActor, or %NULL
450 */
451 ClutterActor *
452 st_bin_get_child (StBin *bin)
453 {
454 g_return_val_if_fail (ST_IS_BIN (bin), NULL);
455
456 return bin->priv->child;
457 }
458
459 /**
460 * st_bin_set_alignment:
461 * @bin: a #StBin
462 * @x_align: horizontal alignment
463 * @y_align: vertical alignment
464 *
465 * Sets the horizontal and vertical alignment of the child
466 * inside a #StBin.
467 */
468 void
469 st_bin_set_alignment (StBin *bin,
470 StAlign x_align,
471 StAlign y_align)
472 {
473 StBinPrivate *priv;
474 gboolean changed = FALSE;
475
476 g_return_if_fail (ST_IS_BIN (bin));
477
478 priv = bin->priv;
479
480 g_object_freeze_notify (G_OBJECT (bin));
481
482 if (priv->x_align != x_align)
483 {
484 priv->x_align = x_align;
485 g_object_notify (G_OBJECT (bin), "x-align");
486 changed = TRUE;
487 }
488
489 if (priv->y_align != y_align)
490 {
491 priv->y_align = y_align;
492 g_object_notify (G_OBJECT (bin), "y-align");
493 changed = TRUE;
494 }
495
496 if (changed)
497 clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
498
499 g_object_thaw_notify (G_OBJECT (bin));
500 }
501
502 /**
503 * st_bin_get_alignment:
504 * @bin: a #StBin
505 * @x_align: return location for the horizontal alignment, or %NULL
506 * @y_align: return location for the vertical alignment, or %NULL
507 *
508 * Retrieves the horizontal and vertical alignment of the child
509 * inside a #StBin, as set by st_bin_set_alignment().
510 */
511 void
512 st_bin_get_alignment (StBin *bin,
513 StAlign *x_align,
514 StAlign *y_align)
515 {
516 StBinPrivate *priv;
517
518 g_return_if_fail (ST_IS_BIN (bin));
519
520 priv = bin->priv;
521
522 if (x_align)
523 *x_align = priv->x_align;
524
525 if (y_align)
526 *y_align = priv->y_align;
527 }
528
529 /**
530 * st_bin_set_fill:
531 * @bin: a #StBin
532 * @x_fill: %TRUE if the child should fill horizontally the @bin
533 * @y_fill: %TRUE if the child should fill vertically the @bin
534 *
535 * Sets whether the child of @bin should fill out the horizontal
536 * and/or vertical allocation of the parent
537 */
538 void
539 st_bin_set_fill (StBin *bin,
540 gboolean x_fill,
541 gboolean y_fill)
542 {
543 StBinPrivate *priv;
544 gboolean changed = FALSE;
545
546 g_return_if_fail (ST_IS_BIN (bin));
547
548 priv = bin->priv;
549
550 g_object_freeze_notify (G_OBJECT (bin));
551
552 if (priv->x_fill != x_fill)
553 {
554 priv->x_fill = x_fill;
555 changed = TRUE;
556
557 g_object_notify (G_OBJECT (bin), "x-fill");
558 }
559
560 if (priv->y_fill != y_fill)
561 {
562 priv->y_fill = y_fill;
563 changed = TRUE;
564
565 g_object_notify (G_OBJECT (bin), "y-fill");
566 }
567
568 if (changed)
569 clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
570
571 g_object_thaw_notify (G_OBJECT (bin));
572 }
573
574 /**
575 * st_bin_get_fill:
576 * @bin: a #StBin
577 * @x_fill: (out): return location for the horizontal fill, or %NULL
578 * @y_fill: (out): return location for the vertical fill, or %NULL
579 *
580 * Retrieves the horizontal and vertical fill settings
581 */
582 void
583 st_bin_get_fill (StBin *bin,
584 gboolean *x_fill,
585 gboolean *y_fill)
586 {
587 g_return_if_fail (ST_IS_BIN (bin));
588
589 if (x_fill)
590 *x_fill = bin->priv->x_fill;
591
592 if (y_fill)
593 *y_fill = bin->priv->y_fill;
594 }