#include #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_CONTAINER) static void aspect_bin_init(AspectBin *abin) { GTK_WIDGET_SET_FLAGS(abin, GTK_NO_WINDOW); abin->body = NULL; abin->side = NULL; abin->ratio = 1; abin->align = 0; abin->constrain = FALSE; } static void aspect_bin_class_init(AspectBinClass *class) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class); 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->body == child) { aspect_bin_set_body(abin, NULL, 1); } else if (abin->side == child) { aspect_bin_set_side(abin, NULL); } } static void aspect_bin_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { AspectBin *abin = ASPECT_BIN(container); g_return_if_fail(callback != NULL); if (abin->body) callback(abin->body, callback_data); if (abin->side) callback(abin->side, callback_data); } static void aspect_bin_size_request(GtkWidget *widget, GtkRequisition *requisition) { AspectBin *abin = ASPECT_BIN(widget); GtkRequisition creq = {0}, areq = {0}; if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) { gtk_widget_size_request(abin->side, &creq); } if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) { int wtmp, htmp; gtk_widget_size_request(abin->body, &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); GtkRequisition creq = {0}; GtkAllocation csize = {0}, asize = {0}; /* First find the best fit for the body. */ if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) { 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; } } /* Now try to fit the side. */ if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) { gtk_widget_get_child_requisition(abin->side, &creq); if (allocation->width - asize.width < creq.width) { /* It didn't fit, squish the constrained guy. */ asize.width = allocation->width - creq.width; asize.height = asize.width / abin->ratio + 0.5; } csize.width = allocation->width - asize.width; csize.height = MIN(allocation->height, creq.height); if (abin->constrain) { csize.height = MAX(csize.height, asize.height); } csize.x = asize.width; } csize.y = (allocation->height - csize.height) * abin->align + 0.5; asize.y = (allocation->height - asize.height) * abin->align + 0.5; if (abin->body) gtk_widget_size_allocate(abin->body, &asize); if (abin->side) gtk_widget_size_allocate(abin->side, &csize); } static gboolean set_widget(GtkWidget **dest, GtkWidget *parent, GtkWidget *widget) { gboolean need_resize = FALSE; if (*dest == widget) return FALSE; if (*dest) { need_resize |= GTK_WIDGET_VISIBLE(*dest); gtk_widget_unparent(*dest); } *dest = widget; if (widget) { gtk_widget_set_parent(widget, parent); need_resize |= GTK_WIDGET_VISIBLE(widget); } return GTK_WIDGET_VISIBLE(parent) && need_resize; } void aspect_bin_set_body(AspectBin *abin, GtkWidget *widget, gfloat ratio) { g_return_if_fail(IS_ASPECT_BIN(abin)); g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget)); g_return_if_fail(widget == NULL || widget->parent == NULL); if (set_widget(&abin->body, GTK_WIDGET(abin), widget)) gtk_widget_queue_resize(GTK_WIDGET(abin)); } void aspect_bin_set_side(AspectBin *abin, GtkWidget *widget) { g_return_if_fail(IS_ASPECT_BIN(abin)); g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget)); g_return_if_fail(widget == NULL || widget->parent == NULL); if (set_widget(&abin->side, GTK_WIDGET(abin), widget)) gtk_widget_queue_resize(GTK_WIDGET(abin)); }