1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
* mx-aspect-frame.c: A container that respect the aspect ratio of its child
5
* Copyright 2010, 2011 Intel Corporation.
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms and conditions of the GNU Lesser General Public License,
9
* version 2.1, as published by the Free Software Foundation.
11
* This program is distributed in the hope it will be useful, but WITHOUT ANY
12
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this program; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
19
* Boston, MA 02111-1307, USA.
22
#include "cheese-aspect-frame.h"
24
G_DEFINE_TYPE (CheeseAspectFrame, cheese_aspect_frame, MX_TYPE_BIN)
26
#define ASPECT_FRAME_PRIVATE(o) \
27
(G_TYPE_INSTANCE_GET_PRIVATE ((o), \
28
CHEESE_TYPE_ASPECT_FRAME, \
29
CheeseAspectFramePrivate))
39
struct _CheeseAspectFramePrivate
48
cheese_aspect_frame_get_property (GObject *object,
53
CheeseAspectFrame *frame = CHEESE_ASPECT_FRAME (object);
58
g_value_set_boolean (value, cheese_aspect_frame_get_expand (frame));
62
g_value_set_float (value, cheese_aspect_frame_get_ratio (frame));
66
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
71
cheese_aspect_frame_set_property (GObject *object,
79
cheese_aspect_frame_set_expand (CHEESE_ASPECT_FRAME (object),
80
g_value_get_boolean (value));
84
cheese_aspect_frame_set_ratio (CHEESE_ASPECT_FRAME (object),
85
g_value_get_float (value));
89
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
94
cheese_aspect_frame_dispose (GObject *object)
96
G_OBJECT_CLASS (cheese_aspect_frame_parent_class)->dispose (object);
100
cheese_aspect_frame_finalize (GObject *object)
102
G_OBJECT_CLASS (cheese_aspect_frame_parent_class)->finalize (object);
106
cheese_aspect_frame_get_preferred_width (ClutterActor *actor,
114
mx_widget_get_padding (MX_WIDGET (actor), &padding);
116
for_height = MAX (0, for_height - padding.top - padding.bottom);
121
g_object_get (G_OBJECT (actor), "natural-height-set", &override, NULL);
124
g_object_get (G_OBJECT (actor), "natural-height", &for_height, NULL);
126
CLUTTER_ACTOR_CLASS (cheese_aspect_frame_parent_class)->
127
get_preferred_width (actor, for_height, min_width_p, nat_width_p);
130
*min_width_p += padding.left + padding.right;
132
*nat_width_p += padding.left + padding.right;
136
cheese_aspect_frame_get_preferred_height (ClutterActor *actor,
138
gfloat *min_height_p,
139
gfloat *nat_height_p)
144
mx_widget_get_padding (MX_WIDGET (actor), &padding);
146
for_width = MAX (0, for_width - padding.left - padding.right);
151
g_object_get (G_OBJECT (actor), "natural-width-set", &override, NULL);
154
g_object_get (G_OBJECT (actor), "natural-width", &for_width, NULL);
156
CLUTTER_ACTOR_CLASS (cheese_aspect_frame_parent_class)->
157
get_preferred_height (actor, for_width, min_height_p, nat_height_p);
161
cheese_aspect_frame_allocate (ClutterActor *actor,
162
const ClutterActorBox *box,
163
ClutterAllocationFlags flags)
167
ClutterActorBox child_box;
168
gfloat aspect, child_aspect, width, height, box_width, box_height;
170
CheeseAspectFramePrivate *priv = CHEESE_ASPECT_FRAME (actor)->priv;
172
CLUTTER_ACTOR_CLASS (cheese_aspect_frame_parent_class)->
173
allocate (actor, box, flags);
175
child = mx_bin_get_child (MX_BIN (actor));
179
mx_widget_get_padding (MX_WIDGET (actor), &padding);
181
box_width = box->x2 - box->x1 - padding.left - padding.right;
182
box_height = box->y2 - box->y1 - padding.top - padding.bottom;
183
clutter_actor_get_preferred_size (child, NULL, NULL, &width, &height);
185
aspect = box_width / box_height;
186
if (priv->ratio >= 0.f)
187
child_aspect = priv->ratio;
189
child_aspect = width / height;
191
if ((aspect < child_aspect) ^ priv->expand)
194
height = box_width / child_aspect;
199
width = box_height * child_aspect;
202
child_box.x1 = (box_width - width) / 2 + padding.left;
203
child_box.y1 = (box_height - height) / 2 + padding.top;
204
child_box.x2 = child_box.x1 + width;
205
child_box.y2 = child_box.y1 + height;
207
clutter_actor_allocate (child, &child_box, flags);
211
cheese_aspect_frame_paint (ClutterActor *actor)
213
ClutterActor *child = mx_bin_get_child (MX_BIN (actor));
214
CheeseAspectFramePrivate *priv = CHEESE_ASPECT_FRAME (actor)->priv;
222
gfloat width, height;
224
clutter_actor_get_size (actor, &width, &height);
225
mx_widget_get_padding (MX_WIDGET (actor), &padding);
227
/* Special-case textures and just munge their coordinates.
228
* This avoids clipping, which can break Clutter's batching.
230
if (CLUTTER_IS_TEXTURE (child))
236
clutter_actor_get_position (child, &x, &y);
239
clutter_texture_get_cogl_material (CLUTTER_TEXTURE (child));
240
opacity = clutter_actor_get_paint_opacity (child);
241
cogl_material_set_color4ub (material,
242
opacity, opacity, opacity, opacity);
243
cogl_set_source (material);
247
width -= padding.left + padding.right;
248
height -= padding.top + padding.bottom;
250
tx = (width / (width - (x * 2.f))) / 2.f;
251
ty = (height / (height - (y * 2.f))) / 2.f;
253
cogl_rectangle_with_texture_coords (padding.left, padding.top,
254
padding.left + width,
255
padding.top + height,
256
0.5f - tx, 0.5f - ty,
257
0.5f + tx, 0.5f + ty);
261
cogl_clip_push_rectangle (padding.left, padding.top,
262
padding.left + width, padding.top + height);
263
clutter_actor_paint (child);
268
clutter_actor_paint (child);
272
cheese_aspect_frame_pick (ClutterActor *actor,
273
const ClutterColor *color)
277
ClutterActor *child = mx_bin_get_child (MX_BIN (actor));
278
CheeseAspectFramePrivate *priv = CHEESE_ASPECT_FRAME (actor)->priv;
280
clutter_actor_get_allocation_box (actor, &box);
282
cogl_set_source_color4ub (color->red, color->green,
283
color->blue, color->alpha);
284
cogl_rectangle (box.x1, box.y1, box.x2, box.y2);
292
mx_widget_get_padding (MX_WIDGET (actor), &padding);
294
cogl_clip_push_rectangle (padding.left, padding.top,
295
padding.left + (box.x2 - box.x1),
296
padding.top + (box.y2 - box.y1));
297
clutter_actor_paint (child);
301
clutter_actor_paint (child);
305
cheese_aspect_frame_class_init (CheeseAspectFrameClass *klass)
309
GObjectClass *object_class = G_OBJECT_CLASS (klass);
310
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
312
g_type_class_add_private (klass, sizeof (CheeseAspectFramePrivate));
314
object_class->get_property = cheese_aspect_frame_get_property;
315
object_class->set_property = cheese_aspect_frame_set_property;
316
object_class->dispose = cheese_aspect_frame_dispose;
317
object_class->finalize = cheese_aspect_frame_finalize;
319
actor_class->get_preferred_width = cheese_aspect_frame_get_preferred_width;
320
actor_class->get_preferred_height = cheese_aspect_frame_get_preferred_height;
321
actor_class->allocate = cheese_aspect_frame_allocate;
322
actor_class->paint = cheese_aspect_frame_paint;
323
actor_class->pick = cheese_aspect_frame_pick;
325
pspec = g_param_spec_boolean ("expand",
327
"Fill the allocated area with the child and "
328
"clip off the excess.",
330
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
331
g_object_class_install_property (object_class, PROP_EXPAND, pspec);
333
pspec = g_param_spec_float ("ratio",
335
"Override the child's aspect ratio "
337
-1.f, G_MAXFLOAT, -1.f,
338
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
339
g_object_class_install_property (object_class, PROP_RATIO, pspec);
343
cheese_aspect_frame_init (CheeseAspectFrame *self)
345
CheeseAspectFramePrivate *priv = self->priv = ASPECT_FRAME_PRIVATE (self);
350
cheese_aspect_frame_new (void)
352
return g_object_new (CHEESE_TYPE_ASPECT_FRAME, NULL);
356
cheese_aspect_frame_set_expand (CheeseAspectFrame *frame, gboolean expand)
358
CheeseAspectFramePrivate *priv;
360
g_return_if_fail (CHEESE_IS_ASPECT_FRAME (frame));
363
if (priv->expand != expand)
365
priv->expand = expand;
366
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
367
g_object_notify (G_OBJECT (frame), "expand");
372
cheese_aspect_frame_get_expand (CheeseAspectFrame *frame)
374
g_return_val_if_fail (CHEESE_IS_ASPECT_FRAME (frame), FALSE);
375
return frame->priv->expand;
379
cheese_aspect_frame_set_ratio (CheeseAspectFrame *frame, gfloat ratio)
381
CheeseAspectFramePrivate *priv;
383
g_return_if_fail (CHEESE_IS_ASPECT_FRAME (frame));
386
if (priv->ratio != ratio)
389
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
390
g_object_notify (G_OBJECT (frame), "ratio");
395
cheese_aspect_frame_get_ratio (CheeseAspectFrame *frame)
397
g_return_val_if_fail (CHEESE_IS_ASPECT_FRAME (frame), -1.f);
398
return frame->priv->ratio;