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