--- /dev/null
+#include <stdio.h>
+#include "aspectbin.h"
+
+static void aspect_bin_size_request(GtkWidget *, GtkRequisition *);
+static void aspect_bin_size_allocate(GtkWidget *, GtkAllocation *);
+static void aspect_bin_remove(GtkContainer *, GtkWidget *);
+static void aspect_bin_forall(GtkContainer *, gboolean, GtkCallback, gpointer);
+
+G_DEFINE_TYPE(AspectBin, aspect_bin, GTK_TYPE_BIN)
+
+static void aspect_bin_init(AspectBin *ab)
+{
+ ab->child = NULL;
+ ab->ratio = 1;
+}
+
+static void aspect_bin_class_init(AspectBinClass *abc)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(abc);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS(abc);
+
+ widget_class->size_request = aspect_bin_size_request;
+ widget_class->size_allocate = aspect_bin_size_allocate;
+
+ container_class->remove = aspect_bin_remove;
+ container_class->forall = aspect_bin_forall;
+}
+
+GtkWidget *aspect_bin_new(void)
+{
+ return GTK_WIDGET(g_object_new(ASPECT_BIN_TYPE, NULL));
+}
+
+static void aspect_bin_remove(GtkContainer *container, GtkWidget *child)
+{
+ AspectBin *abin = ASPECT_BIN(container);
+
+ if (abin->child == child) {
+ aspect_bin_set_body_widget(abin, NULL, 1);
+ } else {
+ GTK_CONTAINER_CLASS(aspect_bin_parent_class)->remove
+ (container, child);
+ }
+}
+
+static void aspect_bin_forall(GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ AspectBin *abin = ASPECT_BIN(container);
+ GtkBin *bin = GTK_BIN(container);
+
+ if (bin->child)
+ callback(bin->child, callback_data);
+
+ if (abin->child)
+ callback(abin->child, callback_data);
+}
+
+static void
+aspect_bin_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+ AspectBin *abin = ASPECT_BIN(widget);
+ GtkBin *bin = GTK_BIN(widget);
+ GtkRequisition creq = {0}, areq = {0};
+
+ if (bin->child && GTK_WIDGET_VISIBLE(bin->child)) {
+ gtk_widget_size_request(bin->child, &creq);
+ }
+
+ if (abin->child && GTK_WIDGET_VISIBLE(abin->child)) {
+ int wtmp, htmp;
+ gtk_widget_size_request(abin->child, &areq);
+ wtmp = areq.height * abin->ratio + 0.5;
+ htmp = areq.width / abin->ratio + 0.5;
+
+ if (wtmp > areq.width) {
+ areq.width = wtmp;
+ } else {
+ areq.height = htmp;
+ }
+ }
+
+ requisition->width = areq.width + creq.width;
+ requisition->height = MAX(areq.height, creq.height);
+}
+
+static void
+aspect_bin_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ AspectBin *abin = ASPECT_BIN(widget);
+ GtkBin *bin = GTK_BIN(widget);
+ GtkRequisition creq = {0};
+ GtkAllocation csize = {0}, asize = {0};
+
+ if (abin->child && GTK_WIDGET_VISIBLE(abin->child)) {
+ asize.height = allocation->height;
+ asize.width = asize.height * abin->ratio + 0.5;
+
+ if (asize.width > allocation->width) {
+ asize.width = allocation->width;
+ asize.height = asize.width / abin->ratio + 0.5;
+ }
+ }
+
+ if (bin->child && GTK_WIDGET_VISIBLE(bin->child)) {
+ gtk_widget_get_child_requisition(bin->child, &creq);
+
+ if (allocation->width - asize.width < creq.width) {
+ asize.width = allocation->width - creq.width;
+ asize.height = asize.width / abin->ratio + 0.5;
+ }
+
+ csize.width = allocation->width - asize.width;
+ csize.height = allocation->height;
+ csize.x = asize.width;
+ csize.y = 0;
+ }
+
+ if (bin->child)
+ gtk_widget_size_allocate(bin->child, &csize);
+ if (abin->child)
+ gtk_widget_size_allocate(abin->child, &asize);
+}
+
+void
+aspect_bin_set_body_widget(AspectBin *ab, GtkWidget *widget, gfloat ratio)
+{
+ gboolean need_resize = FALSE;
+
+ g_return_if_fail(IS_ASPECT_BIN(ab));
+ g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
+ g_return_if_fail(widget == NULL || widget->parent == NULL);
+
+ if (ab->child == widget)
+ return;
+
+ if (ab->child) {
+ need_resize |= GTK_WIDGET_VISIBLE(ab->child);
+ gtk_widget_unparent(ab->child);
+ }
+
+ ab->child = widget;
+ if (widget) {
+ gtk_widget_set_parent(widget, GTK_WIDGET(ab));
+ need_resize |= GTK_WIDGET_VISIBLE(widget);
+ }
+
+ if (GTK_WIDGET_VISIBLE(ab) && need_resize)
+ gtk_widget_queue_resize(GTK_WIDGET(ab));
+}
--- /dev/null
+#ifndef ASPECTBIN_H_
+#define ASPECTBIN_H_
+
+#include <gtk/gtk.h>
+
+#define ASPECT_BIN_TYPE (aspect_bin_get_type())
+#define ASPECT_BIN(obj) GTK_CHECK_CAST((obj), ASPECT_BIN_TYPE, AspectBin)
+#define ASPECT_BIN_CLASS(class) \
+ GTK_CHECK_CLASS_CAST((class), ASPECT_BIN_TYPE, AspectBinClass)
+#define IS_ASPECT_BIN(obj) GTK_CHECK_TYPE((obj), ASPECT_BIN_TYPE)
+#define IS_ASPECT_BIN_CLASS(class) \
+ GTK_CHECK_CLASS_TYPE((class), ASPECT_BIN_TYPE, AspectBinClass)
+
+typedef struct AspectBin AspectBin;
+typedef struct AspectBinClass AspectBinClass;
+
+struct AspectBin {
+ GtkBin bin;
+
+ GtkWidget *child;
+ gfloat ratio;
+};
+
+struct AspectBinClass {
+ GtkBinClass parent_class;
+};
+
+GType aspect_bin_get_type(void);
+GtkWidget *aspect_bin_new(void);
+void aspect_bin_set_body_widget(AspectBin *, GtkWidget *, gfloat);
+
+#endif
--- /dev/null
+#include <gtk/gtk.h>
+#include "aspectbin.h"
+
+int main(int argc, char **argv)
+{
+ GtkWidget *window;
+ GtkWidget *aspectbin;
+ GtkWidget *button1, *button2;
+
+ gtk_init(&argc, &argv);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window), "Blargh");
+ gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
+
+ aspectbin = aspect_bin_new();
+ button1 = gtk_button_new_with_label("Square");
+ button2 = gtk_button_new_with_label("Rectangle");
+ aspect_bin_set_body_widget(ASPECT_BIN(aspectbin), button1, 1);
+ gtk_container_add(GTK_CONTAINER(aspectbin), button2);
+
+ gtk_container_add(GTK_CONTAINER(window), aspectbin);
+
+ gtk_widget_show_all(window);
+ gtk_main();
+
+ return 0;
+}