15 static void aspect_bin_size_request(GtkWidget *, GtkRequisition *);
16 static void aspect_bin_size_allocate(GtkWidget *, GtkAllocation *);
17 static void aspect_bin_add(GtkContainer *, GtkWidget *);
18 static void aspect_bin_remove(GtkContainer *, GtkWidget *);
19 static void aspect_bin_forall(GtkContainer *, gboolean, GtkCallback, gpointer);
20 static GType aspect_bin_child_type(GtkContainer *);
22 static void aspect_bin_get_property(GObject *, guint, GValue *, GParamSpec *);
23 static void aspect_bin_set_property(GObject *, guint, const GValue *,
25 static void aspect_bin_set_child_property(GtkContainer *, GtkWidget *, guint,
26 const GValue *, GParamSpec *);
27 static void aspect_bin_get_child_property(GtkContainer *, GtkWidget *, guint,
28 GValue *, GParamSpec *);
30 G_DEFINE_TYPE(AspectBin, aspect_bin, GTK_TYPE_CONTAINER)
32 static void aspect_bin_init(AspectBin *abin)
34 GTK_WIDGET_SET_FLAGS(abin, GTK_NO_WINDOW);
39 abin->body_align = 0.5;
40 abin->side_align = 0.5;
41 abin->constrain = FALSE;
45 static void aspect_bin_class_init(AspectBinClass *class)
47 GObjectClass *object_class = G_OBJECT_CLASS(class);
48 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
49 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class);
51 widget_class->size_request = aspect_bin_size_request;
52 widget_class->size_allocate = aspect_bin_size_allocate;
54 container_class->add = aspect_bin_add;
55 container_class->remove = aspect_bin_remove;
56 container_class->forall = aspect_bin_forall;
57 container_class->child_type = aspect_bin_child_type;
59 container_class->set_child_property = aspect_bin_set_child_property;
60 container_class->get_child_property = aspect_bin_get_child_property;
62 object_class->set_property = aspect_bin_set_property;
63 object_class->get_property = aspect_bin_get_property;
65 g_object_class_install_property(object_class,
67 g_param_spec_boolean("fill",
69 "Allocate all remaining space to the side.",
72 g_object_class_install_property(object_class,
74 g_param_spec_boolean("constrain",
76 "Only expand the side to be as large as the body.",
79 gtk_container_class_install_child_property(container_class,
81 g_param_spec_float("align",
83 "Alignment of the child within the available space.",
88 GtkWidget *aspect_bin_new(void)
90 return GTK_WIDGET(g_object_new(ASPECT_BIN_TYPE, NULL));
93 static void aspect_bin_set_property(GObject *object,
98 AspectBin *abin = ASPECT_BIN(object);
102 abin->constrain = g_value_get_boolean(value);
103 gtk_widget_queue_resize(GTK_WIDGET(abin));
106 abin->fill = g_value_get_boolean(value);
107 gtk_widget_queue_resize(GTK_WIDGET(abin));
110 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
114 static void aspect_bin_get_property(GObject *object,
119 AspectBin *abin = ASPECT_BIN(object);
123 g_value_set_boolean(value, abin->constrain);
126 g_value_set_boolean(value, abin->fill);
129 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
133 static void aspect_bin_set_child_property(GtkContainer *container,
139 AspectBin *abin = ASPECT_BIN(container);
142 g_assert(child == abin->body || child == abin->side);
145 case CHILD_PROP_ALIGN:
146 align = g_value_get_float(value);
147 if (child == abin->body)
148 abin->body_align = align;
149 else if (child == abin->side)
150 abin->side_align = align;
151 gtk_widget_queue_resize(GTK_WIDGET(abin));
154 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container,
159 static void aspect_bin_get_child_property(GtkContainer *container,
165 AspectBin *abin = ASPECT_BIN(container);
168 g_assert(child == abin->body || child == abin->side);
171 case CHILD_PROP_ALIGN:
172 if (child == abin->body)
173 align = abin->body_align;
174 else if (child == abin->side)
175 align = abin->side_align;
176 g_value_set_float(value, align);
179 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container,
184 static void aspect_bin_add(GtkContainer *container, GtkWidget *widget)
187 g_return_if_fail(IS_ASPECT_BIN(container));
188 abin = ASPECT_BIN(container);
191 aspect_bin_set_body(abin, widget, 1);
192 else if (!abin->side)
193 aspect_bin_set_side(abin, widget);
195 g_warning("AspectBin cannot have more than 2 children.\n");
198 static void aspect_bin_remove(GtkContainer *container, GtkWidget *child)
200 AspectBin *abin = ASPECT_BIN(container);
202 if (abin->body == child) {
203 aspect_bin_set_body(abin, NULL, 1);
204 } else if (abin->side == child) {
205 aspect_bin_set_side(abin, NULL);
209 static void aspect_bin_forall(GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data)
214 AspectBin *abin = ASPECT_BIN(container);
215 g_return_if_fail(callback != NULL);
218 callback(abin->body, callback_data);
220 callback(abin->side, callback_data);
223 static GType aspect_bin_child_type(GtkContainer *container)
225 if (!ASPECT_BIN(container)->body || !ASPECT_BIN(container)->side)
226 return GTK_TYPE_WIDGET;
231 aspect_bin_size_request(GtkWidget *widget, GtkRequisition *requisition)
233 AspectBin *abin = ASPECT_BIN(widget);
234 GtkRequisition creq = {0}, areq = {0};
236 if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) {
237 gtk_widget_size_request(abin->side, &creq);
240 if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) {
242 gtk_widget_size_request(abin->body, &areq);
243 wtmp = areq.height * abin->ratio + 0.5;
244 htmp = areq.width / abin->ratio + 0.5;
246 if (wtmp > areq.width) {
253 requisition->width = areq.width + creq.width;
254 requisition->height = MAX(areq.height, creq.height);
258 aspect_bin_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
260 AspectBin *abin = ASPECT_BIN(widget);
261 GtkRequisition creq = {0};
262 GtkAllocation csize = {0}, asize = {0};
264 /* First find the best fit for the body. */
265 if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) {
266 asize.height = allocation->height;
267 asize.width = asize.height * abin->ratio + 0.5;
269 if (asize.width > allocation->width) {
270 asize.width = allocation->width;
271 asize.height = asize.width / abin->ratio + 0.5;
275 /* Now try to fit the side. */
276 if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) {
277 gtk_widget_get_child_requisition(abin->side, &creq);
279 if (allocation->width - asize.width < creq.width) {
280 /* It didn't fit, squish the constrained guy. */
281 asize.width = allocation->width - creq.width;
282 asize.height = asize.width / abin->ratio + 0.5;
285 csize.width = allocation->width - asize.width;
286 csize.x = asize.width;
288 if (abin->fill && abin->constrain) {
289 csize.height = asize.height;
290 } else if (abin->fill) {
291 csize.height = allocation->height;
293 csize.height = MIN(creq.height, allocation->height);
297 asize.y = (allocation->height - asize.height) * abin->body_align + 0.5;
298 csize.y = (allocation->height - csize.height) * abin->side_align + 0.5;
300 if (abin->constrain) {
301 csize.y = MIN(asize.y, allocation->height - csize.height);
305 gtk_widget_size_allocate(abin->body, &asize);
307 gtk_widget_size_allocate(abin->side, &csize);
311 set_widget(GtkWidget **dest, GtkWidget *parent, GtkWidget *widget)
313 gboolean need_resize = FALSE;
319 need_resize |= GTK_WIDGET_VISIBLE(*dest);
320 gtk_widget_unparent(*dest);
325 gtk_widget_set_parent(widget, parent);
326 need_resize |= GTK_WIDGET_VISIBLE(widget);
329 return GTK_WIDGET_VISIBLE(parent) && need_resize;
332 void aspect_bin_set_body(AspectBin *abin, GtkWidget *widget, gfloat ratio)
334 g_return_if_fail(IS_ASPECT_BIN(abin));
335 g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
336 g_return_if_fail(widget == NULL || widget->parent == NULL);
338 if (set_widget(&abin->body, GTK_WIDGET(abin), widget))
339 gtk_widget_queue_resize(GTK_WIDGET(abin));
342 void aspect_bin_set_side(AspectBin *abin, GtkWidget *widget)
344 g_return_if_fail(IS_ASPECT_BIN(abin));
345 g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
346 g_return_if_fail(widget == NULL || widget->parent == NULL);
348 if (set_widget(&abin->side, GTK_WIDGET(abin), widget))
349 gtk_widget_queue_resize(GTK_WIDGET(abin));
352 GtkWidget *aspect_bin_get_body(AspectBin *abin)
354 g_return_val_if_fail(IS_ASPECT_BIN(abin), NULL);
358 GtkWidget *aspect_bin_get_side(AspectBin *abin)
360 g_return_val_if_fail(IS_ASPECT_BIN(abin), NULL);