4
* An OpenGL based 'interactive canvas' library.
6
* Copyright (C) 2009 Intel Corporation.
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
22
* Emmanuele Bassi <ebassi@linux.intel.com>
26
* SECTION:clutter-bin-layout
27
* @short_description: A simple layout manager
29
* #ClutterBinLayout is a layout manager which implements the following
33
* <listitem><simpara>the preferred size is the maximum preferred size
34
* between all the children of the container using the
35
* layout;</simpara></listitem>
36
* <listitem><simpara>each child is allocated in "layers", on on top
37
* of the other;</simpara></listitem>
38
* <listitem><simpara>for each layer there are horizontal and vertical
39
* alignment policies.</simpara></listitem>
42
* <figure id="bin-layout">
43
* <title>Bin layout</title>
44
* <para>The image shows a #ClutterBinLayout with three layers:
45
* a background #ClutterCairoTexture, set to fill on both the X
46
* and Y axis; a #ClutterTexture, set to center on both the X and
47
* Y axis; and a #ClutterRectangle, set to %CLUTTER_BIN_ALIGNMENT_END
48
* on both the X and Y axis.</para>
49
* <graphic fileref="bin-layout.png" format="PNG"/>
52
* <example id="example-clutter-bin-layout">
53
* <title>How to pack actors inside a BinLayout</title>
54
* <para>The following code shows how to build a composite actor with
55
* a texture and a background, and add controls overlayed on top. The
56
* background is set to fill the whole allocation, whilst the texture
57
* is centered; there is a control in the top right corner and a label
58
* in the bottom, filling out the whole allocated width.</para>
60
* ClutterLayoutManager *manager;
63
* /* create the layout first */
64
* layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
65
* CLUTTER_BIN_ALIGNMENT_CENTER);
66
* box = clutter_box_new (layout); /* then the container */
68
* /* we can use the layout object to add actors */
69
* clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), background,
70
* CLUTTER_BIN_ALIGNMENT_FILL,
71
* CLUTTER_BIN_ALIGNMENT_FILL);
72
* clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), icon,
73
* CLUTTER_BIN_ALIGNMENT_CENTER,
74
* CLUTTER_BIN_ALIGNMENT_CENTER);
76
* /* align to the bottom left */
77
* clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), label,
78
* CLUTTER_BIN_ALIGNMENT_START,
79
* CLUTTER_BIN_ALIGNMENT_END);
80
* /* align to the top right */
81
* clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout), button,
82
* CLUTTER_BIN_ALIGNMENT_END,
83
* CLUTTER_BIN_ALIGNMENT_START);
87
* #ClutterBinLayout is available since Clutter 1.2
96
#include "clutter-actor.h"
97
#include "clutter-animatable.h"
98
#include "clutter-bin-layout.h"
99
#include "clutter-child-meta.h"
100
#include "clutter-debug.h"
101
#include "clutter-enum-types.h"
102
#include "clutter-layout-meta.h"
103
#include "clutter-private.h"
105
#define CLUTTER_TYPE_BIN_LAYER (clutter_bin_layer_get_type ())
106
#define CLUTTER_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYER, ClutterBinLayer))
107
#define CLUTTER_IS_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYER))
109
#define CLUTTER_BIN_LAYOUT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutPrivate))
111
typedef struct _ClutterBinLayer ClutterBinLayer;
112
typedef struct _ClutterLayoutMetaClass ClutterBinLayerClass;
114
struct _ClutterBinLayoutPrivate
116
ClutterBinAlignment x_align;
117
ClutterBinAlignment y_align;
119
ClutterContainer *container;
122
struct _ClutterBinLayer
124
ClutterLayoutMeta parent_instance;
126
ClutterBinAlignment x_align;
127
ClutterBinAlignment y_align;
146
G_DEFINE_TYPE (ClutterBinLayer,
148
CLUTTER_TYPE_LAYOUT_META);
150
G_DEFINE_TYPE (ClutterBinLayout,
152
CLUTTER_TYPE_LAYOUT_MANAGER);
159
set_layer_x_align (ClutterBinLayer *self,
160
ClutterBinAlignment alignment)
162
ClutterLayoutManager *manager;
163
ClutterLayoutMeta *meta;
165
if (self->x_align == alignment)
168
self->x_align = alignment;
170
meta = CLUTTER_LAYOUT_META (self);
171
manager = clutter_layout_meta_get_manager (meta);
172
clutter_layout_manager_layout_changed (manager);
174
g_object_notify (G_OBJECT (self), "x-align");
178
set_layer_y_align (ClutterBinLayer *self,
179
ClutterBinAlignment alignment)
181
ClutterLayoutManager *manager;
182
ClutterLayoutMeta *meta;
184
if (self->y_align == alignment)
187
self->y_align = alignment;
189
meta = CLUTTER_LAYOUT_META (self);
190
manager = clutter_layout_meta_get_manager (meta);
191
clutter_layout_manager_layout_changed (manager);
193
g_object_notify (G_OBJECT (self), "y-align");
197
clutter_bin_layer_set_property (GObject *gobject,
202
ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject);
206
case PROP_LAYER_X_ALIGN:
207
set_layer_x_align (layer, g_value_get_enum (value));
210
case PROP_LAYER_Y_ALIGN:
211
set_layer_y_align (layer, g_value_get_enum (value));
215
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
221
clutter_bin_layer_get_property (GObject *gobject,
226
ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject);
230
case PROP_LAYER_X_ALIGN:
231
g_value_set_enum (value, layer->x_align);
234
case PROP_LAYER_Y_ALIGN:
235
g_value_set_enum (value, layer->y_align);
239
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
245
clutter_bin_layer_class_init (ClutterBinLayerClass *klass)
247
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
250
gobject_class->set_property = clutter_bin_layer_set_property;
251
gobject_class->get_property = clutter_bin_layer_get_property;
253
pspec = g_param_spec_enum ("x-align",
254
"Horizontal Alignment",
255
"Horizontal alignment for the actor "
257
CLUTTER_TYPE_BIN_ALIGNMENT,
258
CLUTTER_BIN_ALIGNMENT_CENTER,
259
CLUTTER_PARAM_READWRITE);
260
g_object_class_install_property (gobject_class,
264
pspec = g_param_spec_enum ("y-align",
265
"Vertical Alignment",
266
"Vertical alignment for the actor "
267
"inside the layer manager",
268
CLUTTER_TYPE_BIN_ALIGNMENT,
269
CLUTTER_BIN_ALIGNMENT_CENTER,
270
CLUTTER_PARAM_READWRITE);
271
g_object_class_install_property (gobject_class,
277
clutter_bin_layer_init (ClutterBinLayer *layer)
279
layer->x_align = CLUTTER_BIN_ALIGNMENT_CENTER;
280
layer->y_align = CLUTTER_BIN_ALIGNMENT_CENTER;
288
set_x_align (ClutterBinLayout *self,
289
ClutterBinAlignment alignment)
291
ClutterBinLayoutPrivate *priv = self->priv;
293
if (priv->x_align != alignment)
295
ClutterLayoutManager *manager;
297
priv->x_align = alignment;
299
manager = CLUTTER_LAYOUT_MANAGER (self);
300
clutter_layout_manager_layout_changed (manager);
302
g_object_notify (G_OBJECT (self), "x-align");
307
set_y_align (ClutterBinLayout *self,
308
ClutterBinAlignment alignment)
310
ClutterBinLayoutPrivate *priv = self->priv;
312
if (priv->y_align != alignment)
314
ClutterLayoutManager *manager;
316
priv->y_align = alignment;
318
manager = CLUTTER_LAYOUT_MANAGER (self);
319
clutter_layout_manager_layout_changed (manager);
321
g_object_notify (G_OBJECT (self), "y-align");
326
clutter_bin_layout_get_preferred_width (ClutterLayoutManager *manager,
327
ClutterContainer *container,
332
GList *children = clutter_container_get_children (container);
334
gfloat min_width, nat_width;
336
min_width = nat_width = 0.0;
338
for (l = children; l != NULL; l = l->next)
340
ClutterActor *child = l->data;
341
gfloat minimum, natural;
343
clutter_actor_get_preferred_width (child, for_height,
347
min_width = MAX (min_width, minimum);
348
nat_width = MAX (nat_width, natural);
352
*min_width_p = min_width;
355
*nat_width_p = nat_width;
359
clutter_bin_layout_get_preferred_height (ClutterLayoutManager *manager,
360
ClutterContainer *container,
362
gfloat *min_height_p,
363
gfloat *nat_height_p)
365
GList *children = clutter_container_get_children (container);
367
gfloat min_height, nat_height;
369
min_height = nat_height = 0.0;
371
for (l = children; l != NULL; l = l->next)
373
ClutterActor *child = l->data;
374
gfloat minimum, natural;
376
clutter_actor_get_preferred_height (child, for_width,
380
min_height = MAX (min_height, minimum);
381
nat_height = MAX (nat_height, natural);
385
*min_height_p = min_height;
388
*nat_height_p = nat_height;
392
get_bin_alignment_factor (ClutterBinAlignment alignment)
396
case CLUTTER_BIN_ALIGNMENT_CENTER:
399
case CLUTTER_BIN_ALIGNMENT_START:
402
case CLUTTER_BIN_ALIGNMENT_END:
405
case CLUTTER_BIN_ALIGNMENT_FIXED:
406
case CLUTTER_BIN_ALIGNMENT_FILL:
414
clutter_bin_layout_allocate (ClutterLayoutManager *manager,
415
ClutterContainer *container,
416
const ClutterActorBox *allocation,
417
ClutterAllocationFlags flags)
419
GList *children = clutter_container_get_children (container);
421
gfloat available_w, available_h;
423
available_w = clutter_actor_box_get_width (allocation);
424
available_h = clutter_actor_box_get_height (allocation);
426
for (l = children; l != NULL; l = l->next)
428
ClutterActor *child = l->data;
429
ClutterLayoutMeta *meta;
430
ClutterBinLayer *layer;
431
ClutterActorBox child_alloc = { 0, };
432
gfloat child_width, child_height;
433
ClutterRequestMode request;
435
meta = clutter_layout_manager_get_child_meta (manager,
438
layer = CLUTTER_BIN_LAYER (meta);
440
if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL)
443
child_alloc.x2 = ceilf (available_w);
446
if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
449
child_alloc.y2 = ceilf (available_h);
452
/* if we are filling horizontally and vertically then we
453
* can break here because we already have a full allocation
455
if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL &&
456
layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL)
458
clutter_actor_allocate (child, &child_alloc, flags);
462
child_width = child_height = 0;
463
request = clutter_actor_get_request_mode (child);
464
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
466
gfloat min_width, nat_width;
467
gfloat min_height, nat_height;
469
clutter_actor_get_preferred_width (child, available_h,
472
child_width = CLAMP (nat_width, min_width, available_w);
474
clutter_actor_get_preferred_height (child, child_width,
477
child_height = CLAMP (nat_height, min_height, available_h);
479
else if (request == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT)
481
gfloat min_width, nat_width;
482
gfloat min_height, nat_height;
484
clutter_actor_get_preferred_height (child, available_w,
487
child_height = CLAMP (nat_height, min_height, available_h);
489
clutter_actor_get_preferred_width (child, child_height,
492
child_width = CLAMP (nat_width, min_width, available_w);
495
if (layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED)
497
child_alloc.x1 = ceilf (clutter_actor_get_x (child));
498
child_alloc.x2 = ceilf (child_alloc.x1 + child_width);
502
gdouble x_align = get_bin_alignment_factor (layer->x_align);
504
if (layer->x_align != CLUTTER_BIN_ALIGNMENT_FILL)
506
child_alloc.x1 = ceilf ((available_w - child_width) * x_align);
507
child_alloc.x2 = ceilf (child_alloc.x1 + child_width);
511
if (layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED)
513
child_alloc.y1 = ceilf (clutter_actor_get_y (child));
514
child_alloc.y2 = ceilf (child_alloc.y1 + child_height);
518
gdouble y_align = get_bin_alignment_factor (layer->y_align);
520
if (layer->y_align != CLUTTER_BIN_ALIGNMENT_FILL)
522
child_alloc.y1 = ceilf ((available_h - child_height) * y_align);
523
child_alloc.y2 = ceilf (child_alloc.y1 + child_height);
527
clutter_actor_allocate (child, &child_alloc, flags);
530
g_list_free (children);
534
clutter_bin_layout_get_child_meta_type (ClutterLayoutManager *manager)
536
return CLUTTER_TYPE_BIN_LAYER;
539
static ClutterLayoutMeta *
540
clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager,
541
ClutterContainer *container,
544
ClutterBinLayoutPrivate *priv;
546
priv = CLUTTER_BIN_LAYOUT (manager)->priv;
548
return g_object_new (CLUTTER_TYPE_BIN_LAYER,
549
"container", container,
552
"x-align", priv->x_align,
553
"y_align", priv->y_align,
558
clutter_bin_layout_set_container (ClutterLayoutManager *manager,
559
ClutterContainer *container)
561
ClutterBinLayoutPrivate *priv;
563
priv = CLUTTER_BIN_LAYOUT (manager)->priv;
564
priv->container = container;
568
clutter_bin_layout_set_property (GObject *gobject,
573
ClutterBinLayout *layout = CLUTTER_BIN_LAYOUT (gobject);
578
set_x_align (layout, g_value_get_enum (value));
582
set_y_align (layout, g_value_get_enum (value));
586
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
592
clutter_bin_layout_get_property (GObject *gobject,
597
ClutterBinLayoutPrivate *priv;
599
priv = CLUTTER_BIN_LAYOUT (gobject)->priv;
604
g_value_set_enum (value, priv->x_align);
608
g_value_set_enum (value, priv->y_align);
612
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
618
clutter_bin_layout_class_init (ClutterBinLayoutClass *klass)
620
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
621
ClutterLayoutManagerClass *layout_class =
622
CLUTTER_LAYOUT_MANAGER_CLASS (klass);
625
g_type_class_add_private (klass, sizeof (ClutterBinLayoutPrivate));
627
gobject_class->set_property = clutter_bin_layout_set_property;
628
gobject_class->get_property = clutter_bin_layout_get_property;
631
* ClutterBinLayout:x-align:
633
* The default horizontal alignment policy for actors managed
634
* by the #ClutterBinLayout
638
pspec = g_param_spec_enum ("x-align",
639
"Horizontal Alignment",
640
"Default horizontal alignment for the actors "
641
"inside the layout manager",
642
CLUTTER_TYPE_BIN_ALIGNMENT,
643
CLUTTER_BIN_ALIGNMENT_CENTER,
644
CLUTTER_PARAM_READWRITE);
645
g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
648
* ClutterBinLayout:y-align:
650
* The default vertical alignment policy for actors managed
651
* by the #ClutterBinLayout
655
pspec = g_param_spec_enum ("y-align",
656
"Vertical Alignment",
657
"Default vertical alignment for the actors "
658
"inside the layout manager",
659
CLUTTER_TYPE_BIN_ALIGNMENT,
660
CLUTTER_BIN_ALIGNMENT_CENTER,
661
CLUTTER_PARAM_READWRITE);
662
g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
664
layout_class->get_preferred_width =
665
clutter_bin_layout_get_preferred_width;
666
layout_class->get_preferred_height =
667
clutter_bin_layout_get_preferred_height;
668
layout_class->allocate =
669
clutter_bin_layout_allocate;
670
layout_class->create_child_meta =
671
clutter_bin_layout_create_child_meta;
672
layout_class->get_child_meta_type =
673
clutter_bin_layout_get_child_meta_type;
674
layout_class->set_container =
675
clutter_bin_layout_set_container;
679
clutter_bin_layout_init (ClutterBinLayout *self)
681
self->priv = CLUTTER_BIN_LAYOUT_GET_PRIVATE (self);
683
self->priv->x_align = CLUTTER_BIN_ALIGNMENT_CENTER;
684
self->priv->y_align = CLUTTER_BIN_ALIGNMENT_CENTER;
688
* clutter_bin_layout_new:
689
* @x_align: the default alignment policy to be used on the
691
* @y_align: the default alignment policy to be used on the
694
* Creates a new #ClutterBinLayout layout manager
696
* Return value: the newly created layout manager
700
ClutterLayoutManager *
701
clutter_bin_layout_new (ClutterBinAlignment x_align,
702
ClutterBinAlignment y_align)
704
return g_object_new (CLUTTER_TYPE_BIN_LAYOUT,
711
* clutter_bin_layout_set_alignment:
712
* @self: a #ClutterBinLayout
713
* @child: (allow-none): a child of @container
714
* @x_align: the horizontal alignment policy to be used for the @child
716
* @y_align: the vertical aligment policy to be used on the @child
719
* Sets the horizontal and vertical alignment policies to be applied
720
* to a @child of @self
722
* If @child is %NULL then the @x_align and @y_align values will
723
* be set as the default alignment policies
728
clutter_bin_layout_set_alignment (ClutterBinLayout *self,
730
ClutterBinAlignment x_align,
731
ClutterBinAlignment y_align)
733
ClutterBinLayoutPrivate *priv;
734
ClutterLayoutManager *manager;
735
ClutterLayoutMeta *meta;
737
g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
738
g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
742
if (priv->container == NULL)
746
set_x_align (self, x_align);
747
set_y_align (self, y_align);
750
g_warning ("The layout of type '%s' must be associated to "
751
"a ClutterContainer before setting the alignment "
753
G_OBJECT_TYPE_NAME (self));
758
manager = CLUTTER_LAYOUT_MANAGER (self);
759
meta = clutter_layout_manager_get_child_meta (manager,
762
g_assert (CLUTTER_IS_BIN_LAYER (meta));
764
set_layer_x_align (CLUTTER_BIN_LAYER (meta), x_align);
765
set_layer_y_align (CLUTTER_BIN_LAYER (meta), y_align);
769
* clutter_bin_layout_get_alignment:
770
* @self: a #ClutterBinLayout
771
* @child: (allow-none): a child of @container
772
* @x_align: (out) (allow-none): return location for the horizontal
774
* @y_align: (out) (allow-none): return location for the vertical
777
* Retrieves the horizontal and vertical alignment policies for
780
* If @child is %NULL the default alignment policies will be returned
786
clutter_bin_layout_get_alignment (ClutterBinLayout *self,
788
ClutterBinAlignment *x_align,
789
ClutterBinAlignment *y_align)
791
ClutterBinLayoutPrivate *priv;
792
ClutterLayoutManager *manager;
793
ClutterLayoutMeta *meta;
794
ClutterBinLayer *layer;
796
g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
800
if (priv->container == NULL)
805
*x_align = priv->x_align;
808
*y_align = priv->y_align;
811
g_warning ("The layout of type '%s' must be associated to "
812
"a ClutterContainer before getting the alignment "
814
G_OBJECT_TYPE_NAME (self));
819
manager = CLUTTER_LAYOUT_MANAGER (self);
820
meta = clutter_layout_manager_get_child_meta (manager,
823
g_assert (CLUTTER_IS_BIN_LAYER (meta));
825
layer = CLUTTER_BIN_LAYER (meta);
828
*x_align = layer->x_align;
831
*y_align = layer->y_align;
835
* clutter_bin_layout_add:
836
* @self: a #ClutterBinLayout
837
* @child: a #ClutterActor
838
* @x_align: horizontal alignment policy for @child
839
* @y_align: vertical alignment policy for @child
841
* Adds a #ClutterActor to the container using @self and
842
* sets the alignment policies for it
844
* This function is equivalent to clutter_container_add_actor()
845
* and clutter_layout_manager_child_set_property() but it does not
846
* require a pointer to the #ClutterContainer associated to the
852
clutter_bin_layout_add (ClutterBinLayout *self,
854
ClutterBinAlignment x_align,
855
ClutterBinAlignment y_align)
857
ClutterBinLayoutPrivate *priv;
858
ClutterLayoutManager *manager;
859
ClutterLayoutMeta *meta;
861
g_return_if_fail (CLUTTER_IS_BIN_LAYOUT (self));
862
g_return_if_fail (CLUTTER_IS_ACTOR (child));
866
if (priv->container == NULL)
868
g_warning ("The layout of type '%s' must be associated to "
869
"a ClutterContainer before adding children",
870
G_OBJECT_TYPE_NAME (self));
874
clutter_container_add_actor (priv->container, child);
876
manager = CLUTTER_LAYOUT_MANAGER (self);
877
meta = clutter_layout_manager_get_child_meta (manager,
880
g_assert (CLUTTER_IS_BIN_LAYER (meta));
882
set_layer_x_align (CLUTTER_BIN_LAYER (meta), x_align);
883
set_layer_y_align (CLUTTER_BIN_LAYER (meta), y_align);