1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 * A container that respects the aspect ratio of its child
5 * Copyright 2010, 2011 Intel Corporation.
6 * Copyright 2012, Red Hat, Inc.
8 * Based upon mx-aspect-frame.c
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU Lesser General Public License,
12 * version 2.1, as published by the Free Software Foundation.
14 * This program is distributed in the hope it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "totem-aspect-frame.h"
32 } TotemAspectFramePrivate
;
34 G_DEFINE_TYPE_WITH_PRIVATE (TotemAspectFrame
, totem_aspect_frame
, CLUTTER_TYPE_ACTOR
)
45 totem_aspect_frame_get_property (GObject
*object
,
50 TotemAspectFrame
*frame
= TOTEM_ASPECT_FRAME (object
);
55 g_value_set_boolean (value
, totem_aspect_frame_get_expand (frame
));
59 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
64 totem_aspect_frame_set_property (GObject
*object
,
72 totem_aspect_frame_set_expand (TOTEM_ASPECT_FRAME (object
),
73 g_value_get_boolean (value
));
77 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
82 totem_aspect_frame_dispose (GObject
*object
)
84 G_OBJECT_CLASS (totem_aspect_frame_parent_class
)->dispose (object
);
88 totem_aspect_frame_finalize (GObject
*object
)
90 G_OBJECT_CLASS (totem_aspect_frame_parent_class
)->finalize (object
);
94 totem_aspect_frame_get_preferred_width (ClutterActor
*actor
,
104 g_object_get (G_OBJECT (actor
), "natural-height-set", &override
, NULL
);
107 g_object_get (G_OBJECT (actor
), "natural-height", &for_height
, NULL
);
109 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class
)->
110 get_preferred_width (actor
, for_height
, min_width_p
, nat_width_p
);
114 totem_aspect_frame_get_preferred_height (ClutterActor
*actor
,
116 gfloat
*min_height_p
,
117 gfloat
*nat_height_p
)
124 g_object_get (G_OBJECT (actor
), "natural-width-set", &override
, NULL
);
127 g_object_get (G_OBJECT (actor
), "natural-width", &for_width
, NULL
);
129 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class
)->
130 get_preferred_height (actor
, for_width
, min_height_p
, nat_height_p
);
134 totem_aspect_frame_get_size (TotemAspectFrame
*frame
,
142 clutter_actor_get_allocation_box (CLUTTER_ACTOR (frame
), &box
);
144 if (fmod (rotation
, 180.0) == 90.0)
162 _get_allocation (ClutterActor
*actor
,
168 clutter_actor_get_allocation_box (actor
, &box
);
171 *width
= box
.x2
- box
.x1
;
173 *height
= box
.y2
- box
.y1
;
177 totem_aspect_frame_set_rotation_internal (TotemAspectFrame
*frame
,
181 TotemAspectFramePrivate
*priv
= totem_aspect_frame_get_instance_private (frame
);
183 gfloat frame_width
, frame_height
;
184 gfloat child_width
, child_height
;
185 gfloat child_dest_width
, child_dest_height
;
186 gdouble frame_aspect
;
187 gdouble child_aspect
;
189 actor
= clutter_actor_get_child_at_index (CLUTTER_ACTOR (frame
), 0);
193 totem_aspect_frame_get_size (frame
, rotation
,
194 &frame_width
, &frame_height
);
195 _get_allocation (actor
, &child_width
, &child_height
);
197 if (child_width
<= 0.0f
|| child_height
<= 0.0f
)
200 frame_aspect
= frame_width
/ frame_height
;
201 child_aspect
= child_width
/ child_height
;
203 if ((frame_aspect
< child_aspect
) ^ priv
->expand
)
205 child_dest_width
= frame_width
;
206 child_dest_height
= frame_width
/ child_aspect
;
210 child_dest_height
= frame_height
;
211 child_dest_width
= frame_height
* child_aspect
;
214 clutter_actor_set_pivot_point (actor
, 0.5, 0.5);
218 clutter_actor_save_easing_state (actor
);
219 clutter_actor_set_easing_duration (actor
, 500);
222 clutter_actor_set_rotation_angle (actor
, CLUTTER_Z_AXIS
, rotation
);
223 clutter_actor_set_scale (actor
,
224 child_dest_width
/ child_width
,
225 child_dest_height
/ child_height
);
228 clutter_actor_restore_easing_state (actor
);
232 totem_aspect_frame_allocate (ClutterActor
*actor
,
233 const ClutterActorBox
*box
,
234 ClutterAllocationFlags flags
)
237 ClutterActorBox child_box
;
238 gfloat aspect
, child_aspect
, width
, height
, box_width
, box_height
;
240 TotemAspectFramePrivate
*priv
= totem_aspect_frame_get_instance_private (TOTEM_ASPECT_FRAME (actor
));
242 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class
)->
243 allocate (actor
, box
, flags
);
245 child
= clutter_actor_get_child_at_index (actor
, 0);
249 box_width
= box
->x2
- box
->x1
;
250 box_height
= box
->y2
- box
->y1
;
252 clutter_actor_get_preferred_size (child
, NULL
, NULL
, &width
, &height
);
254 if (width
<= 0.0f
|| height
<= 0.0f
)
257 aspect
= box_width
/ box_height
;
258 child_aspect
= width
/ height
;
260 if ((aspect
< child_aspect
) ^ priv
->expand
)
263 height
= box_width
/ child_aspect
;
268 width
= box_height
* child_aspect
;
271 child_box
.x1
= (box_width
- width
) / 2;
272 child_box
.y1
= (box_height
- height
) / 2;
273 child_box
.x2
= child_box
.x1
+ width
;
274 child_box
.y2
= child_box
.y1
+ height
;
276 clutter_actor_allocate (child
, &child_box
, flags
);
278 totem_aspect_frame_set_rotation_internal (TOTEM_ASPECT_FRAME (actor
),
279 priv
->rotation
, FALSE
);
283 totem_aspect_frame_paint (ClutterActor
*actor
)
286 TotemAspectFramePrivate
*priv
= totem_aspect_frame_get_instance_private (TOTEM_ASPECT_FRAME (actor
));
288 child
= clutter_actor_get_child_at_index (actor
, 0);
295 gfloat width
, height
;
297 clutter_actor_get_size (actor
, &width
, &height
);
299 cogl_clip_push_rectangle (0.0, 0.0, width
, height
);
300 clutter_actor_paint (child
);
304 clutter_actor_paint (child
);
308 totem_aspect_frame_pick (ClutterActor
*actor
,
309 const ClutterColor
*color
)
313 TotemAspectFramePrivate
*priv
= totem_aspect_frame_get_instance_private (TOTEM_ASPECT_FRAME (actor
));
315 clutter_actor_get_allocation_box (actor
, &box
);
317 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class
)->pick (actor
, color
);
319 child
= clutter_actor_get_child_at_index (actor
, 0);
326 cogl_clip_push_rectangle (0.0, 0.0, box
.x2
- box
.x1
, box
.y2
- box
.y1
);
327 clutter_actor_paint (child
);
331 clutter_actor_paint (child
);
335 totem_aspect_frame_class_init (TotemAspectFrameClass
*klass
)
339 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
340 ClutterActorClass
*actor_class
= CLUTTER_ACTOR_CLASS (klass
);
342 object_class
->get_property
= totem_aspect_frame_get_property
;
343 object_class
->set_property
= totem_aspect_frame_set_property
;
344 object_class
->dispose
= totem_aspect_frame_dispose
;
345 object_class
->finalize
= totem_aspect_frame_finalize
;
347 actor_class
->get_preferred_width
= totem_aspect_frame_get_preferred_width
;
348 actor_class
->get_preferred_height
= totem_aspect_frame_get_preferred_height
;
349 actor_class
->allocate
= totem_aspect_frame_allocate
;
350 actor_class
->paint
= totem_aspect_frame_paint
;
351 actor_class
->pick
= totem_aspect_frame_pick
;
353 pspec
= g_param_spec_boolean ("expand",
355 "Fill the allocated area with the child and "
356 "clip off the excess.",
358 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
359 g_object_class_install_property (object_class
, PROP_EXPAND
, pspec
);
363 totem_aspect_frame_init (TotemAspectFrame
*self
)
365 clutter_actor_set_pivot_point (CLUTTER_ACTOR (self
), 0.5f
, 0.5f
);
369 totem_aspect_frame_new (void)
371 return g_object_new (TOTEM_TYPE_ASPECT_FRAME
, NULL
);
375 totem_aspect_frame_set_expand (TotemAspectFrame
*frame
, gboolean expand
)
377 TotemAspectFramePrivate
*priv
;
379 g_return_if_fail (TOTEM_IS_ASPECT_FRAME (frame
));
381 priv
= totem_aspect_frame_get_instance_private (frame
);
382 if (priv
->expand
!= expand
)
384 priv
->expand
= expand
;
385 g_object_notify (G_OBJECT (frame
), "expand");
387 totem_aspect_frame_set_rotation_internal (frame
, priv
->rotation
, TRUE
);
392 totem_aspect_frame_get_expand (TotemAspectFrame
*frame
)
394 TotemAspectFramePrivate
*priv
;
396 g_return_val_if_fail (TOTEM_IS_ASPECT_FRAME (frame
), FALSE
);
397 priv
= totem_aspect_frame_get_instance_private (frame
);
402 totem_aspect_frame_set_child (TotemAspectFrame
*frame
,
405 g_return_if_fail (TOTEM_IS_ASPECT_FRAME (frame
));
407 clutter_actor_add_child (CLUTTER_ACTOR (frame
), child
);
411 totem_aspect_frame_set_rotation (TotemAspectFrame
*frame
,
414 TotemAspectFramePrivate
*priv
;
416 g_return_if_fail (TOTEM_IS_ASPECT_FRAME (frame
));
417 g_return_if_fail (fmod (rotation
, 90.0) == 0.0);
419 priv
= totem_aspect_frame_get_instance_private (frame
);
420 rotation
= fmod (rotation
, 360.0);
422 /* When animating, make sure that we go in the right direction,
423 * otherwise we'll spin in the wrong direction going back to 0 from 270 */
424 if (rotation
== 0.0 && priv
->rotation
== 270.0)
426 else if (rotation
== 90.0 && priv
->rotation
== 360.0)
427 totem_aspect_frame_set_rotation_internal (frame
, 0.0, FALSE
);
428 else if (rotation
== 270.0 && fmod (priv
->rotation
, 360.0) == 0.0)
429 totem_aspect_frame_set_rotation_internal (frame
, 360.0, FALSE
);
431 g_debug ("Setting rotation to '%lf'", rotation
);
433 priv
->rotation
= rotation
;
434 totem_aspect_frame_set_rotation_internal (frame
, rotation
, TRUE
);
438 totem_aspect_frame_get_rotation (TotemAspectFrame
*frame
)
440 TotemAspectFramePrivate
*priv
;
443 g_return_val_if_fail (TOTEM_IS_ASPECT_FRAME (frame
), 0.0);
445 priv
= totem_aspect_frame_get_instance_private (frame
);
446 rotation
= fmod (priv
->rotation
, 360.0);
447 g_debug ("Got rotation %lf", rotation
);