]> git.draconx.ca Git - aspectbin.git/blob - aspectbin.c
Make AspectBin a direct subclass of container.
[aspectbin.git] / aspectbin.c
1 #include <stdio.h>
2 #include "aspectbin.h"
3
4 static void aspect_bin_size_request(GtkWidget *, GtkRequisition *);
5 static void aspect_bin_size_allocate(GtkWidget *, GtkAllocation *);
6 static void aspect_bin_remove(GtkContainer *, GtkWidget *);
7 static void aspect_bin_forall(GtkContainer *, gboolean, GtkCallback, gpointer);
8
9 G_DEFINE_TYPE(AspectBin, aspect_bin, GTK_TYPE_CONTAINER)
10
11 static void aspect_bin_init(AspectBin *abin)
12 {
13         GTK_WIDGET_SET_FLAGS(abin, GTK_NO_WINDOW);
14
15         abin->body      = NULL;
16         abin->side      = NULL;
17         abin->ratio     = 1;
18         abin->align     = 0;
19         abin->constrain = FALSE;
20 }
21
22 static void aspect_bin_class_init(AspectBinClass *class)
23 {
24         GtkWidgetClass    *widget_class    = GTK_WIDGET_CLASS(class);
25         GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class);
26
27         widget_class->size_request  = aspect_bin_size_request;
28         widget_class->size_allocate = aspect_bin_size_allocate;
29
30         container_class->remove = aspect_bin_remove;
31         container_class->forall = aspect_bin_forall;
32 }
33
34 GtkWidget *aspect_bin_new(void)
35 {
36         return GTK_WIDGET(g_object_new(ASPECT_BIN_TYPE, NULL));
37 }
38
39 static void aspect_bin_remove(GtkContainer *container, GtkWidget *child)
40 {
41         AspectBin *abin = ASPECT_BIN(container);
42
43         if (abin->body == child) {
44                 aspect_bin_set_body(abin, NULL, 1);
45         } else if (abin->side == child) {
46                 aspect_bin_set_side(abin, NULL);
47         }
48 }
49
50 static void aspect_bin_forall(GtkContainer *container,
51                               gboolean      include_internals,
52                               GtkCallback   callback,
53                               gpointer      callback_data)
54 {
55         AspectBin *abin = ASPECT_BIN(container);
56         g_return_if_fail(callback != NULL);
57
58         if (abin->body)
59                 callback(abin->body, callback_data);
60         if (abin->side)
61                 callback(abin->side, callback_data);
62 }
63
64 static void
65 aspect_bin_size_request(GtkWidget *widget, GtkRequisition *requisition)
66 {
67         AspectBin *abin = ASPECT_BIN(widget);
68         GtkRequisition creq = {0}, areq = {0};
69
70         if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) {
71                 gtk_widget_size_request(abin->side, &creq);
72         }
73
74         if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) {
75                 int wtmp, htmp;
76                 gtk_widget_size_request(abin->body, &areq);
77                 wtmp = areq.height * abin->ratio + 0.5;
78                 htmp = areq.width  / abin->ratio + 0.5;
79
80                 if (wtmp > areq.width) {
81                         areq.width  = wtmp;
82                 } else {
83                         areq.height = htmp;
84                 }
85         }
86
87         requisition->width  = areq.width + creq.width;
88         requisition->height = MAX(areq.height, creq.height);
89 }
90
91 static void
92 aspect_bin_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
93 {
94         AspectBin *abin = ASPECT_BIN(widget);
95         GtkRequisition creq = {0};
96         GtkAllocation csize = {0}, asize = {0};
97
98         /* First find the best fit for the body. */
99         if (abin->body && GTK_WIDGET_VISIBLE(abin->body)) {
100                 asize.height = allocation->height;
101                 asize.width  = asize.height * abin->ratio + 0.5;
102
103                 if (asize.width > allocation->width) {
104                         asize.width  = allocation->width;
105                         asize.height = asize.width / abin->ratio + 0.5;
106                 }
107         }
108
109         /* Now try to fit the side. */
110         if (abin->side && GTK_WIDGET_VISIBLE(abin->side)) {
111                 gtk_widget_get_child_requisition(abin->side, &creq);
112
113                 if (allocation->width - asize.width < creq.width) {
114                         /* It didn't fit, squish the constrained guy. */
115                         asize.width  = allocation->width - creq.width;
116                         asize.height = asize.width / abin->ratio + 0.5;
117                 }
118
119                 csize.width  = allocation->width - asize.width;
120                 csize.height = MIN(allocation->height, creq.height);
121                 if (abin->constrain) {
122                         csize.height = MAX(csize.height, asize.height);
123                 }
124                 csize.x = asize.width;
125         }
126
127         csize.y = (allocation->height - csize.height) * abin->align + 0.5;
128         asize.y = (allocation->height - asize.height) * abin->align + 0.5;
129
130         if (abin->body)
131                 gtk_widget_size_allocate(abin->body, &asize);
132         if (abin->side)
133                 gtk_widget_size_allocate(abin->side, &csize);
134 }
135
136 static gboolean
137 set_widget(GtkWidget **dest, GtkWidget *parent, GtkWidget *widget)
138 {
139         gboolean need_resize = FALSE;
140
141         if (*dest == widget)
142                 return FALSE;
143
144         if (*dest) {
145                 need_resize |= GTK_WIDGET_VISIBLE(*dest);
146                 gtk_widget_unparent(*dest);
147         }
148
149         *dest = widget;
150         if (widget) {
151                 gtk_widget_set_parent(widget, parent);
152                 need_resize |= GTK_WIDGET_VISIBLE(widget);
153         }
154
155         return GTK_WIDGET_VISIBLE(parent) && need_resize;
156 }
157
158 void aspect_bin_set_body(AspectBin *abin, GtkWidget *widget, gfloat ratio)
159 {
160         g_return_if_fail(IS_ASPECT_BIN(abin));
161         g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
162         g_return_if_fail(widget == NULL || widget->parent == NULL);
163
164         if (set_widget(&abin->body, GTK_WIDGET(abin), widget))
165                 gtk_widget_queue_resize(GTK_WIDGET(abin));
166 }
167
168 void aspect_bin_set_side(AspectBin *abin, GtkWidget *widget)
169 {
170         g_return_if_fail(IS_ASPECT_BIN(abin));
171         g_return_if_fail(widget == NULL || GTK_IS_WIDGET(widget));
172         g_return_if_fail(widget == NULL || widget->parent == NULL);
173
174         if (set_widget(&abin->side, GTK_WIDGET(abin), widget))
175                 gtk_widget_queue_resize(GTK_WIDGET(abin));
176 }