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