1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * GtkAspectFrame: Ensure that the child window has a specified aspect ratio
5 * or, if obey_child, has the same aspect ratio as its requested size
7 * Copyright Owen Taylor 4/9/97
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 * Modified by the GTK+ Team and others 1997-2001. See the AUTHORS
27 * file for a list of people on the GTK+ Team. See the ChangeLog
28 * files for a list of changes. These files are distributed with
29 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
33 * SECTION:gtkaspectframe
34 * @Short_description: A frame that constrains its child to a particular aspect ratio
35 * @Title: GtkAspectFrame
37 * The #GtkAspectFrame is useful when you want
38 * pack a widget so that it can resize but always retains
39 * the same aspect ratio. For instance, one might be
40 * drawing a small preview of a larger image. #GtkAspectFrame
41 * derives from #GtkFrame, so it can draw a label and
42 * a frame around the child. The frame will be
43 * "shrink-wrapped" to the size of the child.
48 #include "gtkaspectframe.h"
50 #include "gtksizerequest.h"
52 #include "gtkprivate.h"
57 struct _GtkAspectFramePrivate
59 GtkAllocation center_allocation;
76 static void gtk_aspect_frame_set_property (GObject *object,
80 static void gtk_aspect_frame_get_property (GObject *object,
84 static void gtk_aspect_frame_compute_child_allocation (GtkFrame *frame,
85 GtkAllocation *child_allocation);
87 #define MAX_RATIO 10000.0
88 #define MIN_RATIO 0.0001
90 G_DEFINE_TYPE (GtkAspectFrame, gtk_aspect_frame, GTK_TYPE_FRAME)
93 gtk_aspect_frame_class_init (GtkAspectFrameClass *class)
95 GObjectClass *gobject_class;
96 GtkFrameClass *frame_class;
98 gobject_class = (GObjectClass*) class;
99 frame_class = (GtkFrameClass*) class;
101 gobject_class->set_property = gtk_aspect_frame_set_property;
102 gobject_class->get_property = gtk_aspect_frame_get_property;
104 frame_class->compute_child_allocation = gtk_aspect_frame_compute_child_allocation;
106 g_object_class_install_property (gobject_class,
108 g_param_spec_float ("xalign",
109 P_("Horizontal Alignment"),
110 P_("X alignment of the child"),
112 GTK_PARAM_READWRITE));
113 g_object_class_install_property (gobject_class,
115 g_param_spec_float ("yalign",
116 P_("Vertical Alignment"),
117 P_("Y alignment of the child"),
119 GTK_PARAM_READWRITE));
120 g_object_class_install_property (gobject_class,
122 g_param_spec_float ("ratio",
124 P_("Aspect ratio if obey_child is FALSE"),
125 MIN_RATIO, MAX_RATIO, 1.0,
126 GTK_PARAM_READWRITE));
127 g_object_class_install_property (gobject_class,
129 g_param_spec_boolean ("obey-child",
131 P_("Force aspect ratio to match that of the frame's child"),
133 GTK_PARAM_READWRITE));
135 g_type_class_add_private (class, sizeof (GtkAspectFramePrivate));
139 gtk_aspect_frame_init (GtkAspectFrame *aspect_frame)
141 GtkAspectFramePrivate *priv;
143 aspect_frame->priv = G_TYPE_INSTANCE_GET_PRIVATE (aspect_frame,
144 GTK_TYPE_ASPECT_FRAME,
145 GtkAspectFramePrivate);
146 priv = aspect_frame->priv;
151 priv->obey_child = TRUE;
155 gtk_aspect_frame_set_property (GObject *object,
160 GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
161 GtkAspectFramePrivate *priv = aspect_frame->priv;
165 /* g_object_notify is handled by the _frame_set function */
167 gtk_aspect_frame_set (aspect_frame,
168 g_value_get_float (value),
174 gtk_aspect_frame_set (aspect_frame,
176 g_value_get_float (value),
181 gtk_aspect_frame_set (aspect_frame,
184 g_value_get_float (value),
187 case PROP_OBEY_CHILD:
188 gtk_aspect_frame_set (aspect_frame,
192 g_value_get_boolean (value));
195 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
201 gtk_aspect_frame_get_property (GObject *object,
206 GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
207 GtkAspectFramePrivate *priv = aspect_frame->priv;
212 g_value_set_float (value, priv->xalign);
215 g_value_set_float (value, priv->yalign);
218 g_value_set_float (value, priv->ratio);
220 case PROP_OBEY_CHILD:
221 g_value_set_boolean (value, priv->obey_child);
224 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230 * gtk_aspect_frame_new:
231 * @label: Label text.
232 * @xalign: Horizontal alignment of the child within the allocation of
233 * the #GtkAspectFrame. This ranges from 0.0 (left aligned)
234 * to 1.0 (right aligned)
235 * @yalign: Vertical alignment of the child within the allocation of
236 * the #GtkAspectFrame. This ranges from 0.0 (left aligned)
237 * to 1.0 (right aligned)
238 * @ratio: The desired aspect ratio.
239 * @obey_child: If %TRUE, @ratio is ignored, and the aspect
240 * ratio is taken from the requistion of the child.
242 * Create a new #GtkAspectFrame.
244 * Returns: the new #GtkAspectFrame.
247 gtk_aspect_frame_new (const gchar *label,
253 GtkAspectFrame *aspect_frame;
254 GtkAspectFramePrivate *priv;
256 aspect_frame = g_object_new (GTK_TYPE_ASPECT_FRAME, NULL);
258 priv = aspect_frame->priv;
260 priv->xalign = CLAMP (xalign, 0.0, 1.0);
261 priv->yalign = CLAMP (yalign, 0.0, 1.0);
262 priv->ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
263 priv->obey_child = obey_child != FALSE;
265 gtk_frame_set_label (GTK_FRAME(aspect_frame), label);
267 return GTK_WIDGET (aspect_frame);
271 * gtk_aspect_frame_set:
272 * @aspect_frame: a #GtkAspectFrame
273 * @xalign: Horizontal alignment of the child within the allocation of
274 * the #GtkAspectFrame. This ranges from 0.0 (left aligned)
275 * to 1.0 (right aligned)
276 * @yalign: Vertical alignment of the child within the allocation of
277 * the #GtkAspectFrame. This ranges from 0.0 (left aligned)
278 * to 1.0 (right aligned)
279 * @ratio: The desired aspect ratio.
280 * @obey_child: If %TRUE, @ratio is ignored, and the aspect
281 * ratio is taken from the requistion of the child.
283 * Set parameters for an existing #GtkAspectFrame.
286 gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
292 GtkAspectFramePrivate *priv;
294 g_return_if_fail (GTK_IS_ASPECT_FRAME (aspect_frame));
296 priv = aspect_frame->priv;
298 xalign = CLAMP (xalign, 0.0, 1.0);
299 yalign = CLAMP (yalign, 0.0, 1.0);
300 ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
301 obey_child = obey_child != FALSE;
303 if (priv->xalign != xalign
304 || priv->yalign != yalign
305 || priv->ratio != ratio
306 || priv->obey_child != obey_child)
308 g_object_freeze_notify (G_OBJECT (aspect_frame));
310 if (priv->xalign != xalign)
312 priv->xalign = xalign;
313 g_object_notify (G_OBJECT (aspect_frame), "xalign");
315 if (priv->yalign != yalign)
317 priv->yalign = yalign;
318 g_object_notify (G_OBJECT (aspect_frame), "yalign");
320 if (priv->ratio != ratio)
323 g_object_notify (G_OBJECT (aspect_frame), "ratio");
325 if (priv->obey_child != obey_child)
327 priv->obey_child = obey_child;
328 g_object_notify (G_OBJECT (aspect_frame), "obey-child");
330 g_object_thaw_notify (G_OBJECT (aspect_frame));
332 gtk_widget_queue_resize (GTK_WIDGET (aspect_frame));
337 gtk_aspect_frame_compute_child_allocation (GtkFrame *frame,
338 GtkAllocation *child_allocation)
340 GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (frame);
341 GtkAspectFramePrivate *priv = aspect_frame->priv;
342 GtkBin *bin = GTK_BIN (frame);
346 child = gtk_bin_get_child (bin);
347 if (child && gtk_widget_get_visible (child))
349 GtkAllocation full_allocation;
351 if (priv->obey_child)
353 GtkRequisition child_requisition;
355 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
356 if (child_requisition.height != 0)
358 ratio = ((gdouble) child_requisition.width /
359 child_requisition.height);
360 if (ratio < MIN_RATIO)
363 else if (child_requisition.width != 0)
371 GTK_FRAME_CLASS (gtk_aspect_frame_parent_class)->compute_child_allocation (frame, &full_allocation);
373 if (ratio * full_allocation.height > full_allocation.width)
375 child_allocation->width = full_allocation.width;
376 child_allocation->height = full_allocation.width / ratio + 0.5;
380 child_allocation->width = ratio * full_allocation.height + 0.5;
381 child_allocation->height = full_allocation.height;
384 child_allocation->x = full_allocation.x + priv->xalign * (full_allocation.width - child_allocation->width);
385 child_allocation->y = full_allocation.y + priv->yalign * (full_allocation.height - child_allocation->height);
388 GTK_FRAME_CLASS (gtk_aspect_frame_parent_class)->compute_child_allocation (frame, child_allocation);