1 /*
2 * Copyright © 2015 Red Hat Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20 #include "config.h"
21
22 #include "gtkboxgadgetprivate.h"
23
24 #include "gtkcssnodeprivate.h"
25 #include "gtkmain.h"
26 #include "gtkprivate.h"
27 #include "gtksizerequest.h"
28 #include "gtkwidgetprivate.h"
29
30 /* GtkBoxGadget is a container gadget implementation that arranges its
31 * children in a row, either horizontally or vertically. Children can
32 * be either widgets or gadgets, and can be set to expand horizontally
33 * or vertically, or both.
34 */
35
36 typedef struct _GtkBoxGadgetPrivate GtkBoxGadgetPrivate;
37 struct _GtkBoxGadgetPrivate {
38 GtkOrientation orientation;
39 GArray *children;
40
41 guint draw_focus : 1;
42 guint draw_reverse : 1;
43 guint allocate_reverse : 1;
44 guint align_reverse : 1;
45 };
46
47 typedef gboolean (* ComputeExpandFunc) (GObject *object, GtkOrientation orientation);
48
49 typedef struct _GtkBoxGadgetChild GtkBoxGadgetChild;
50 struct _GtkBoxGadgetChild {
51 GObject *object;
52 gboolean expand;
53 GtkAlign align;
54 };
55
G_DEFINE_TYPE_WITH_CODE(GtkBoxGadget,gtk_box_gadget,GTK_TYPE_CSS_GADGET,G_ADD_PRIVATE (GtkBoxGadget))56 G_DEFINE_TYPE_WITH_CODE (GtkBoxGadget, gtk_box_gadget, GTK_TYPE_CSS_GADGET,
57 G_ADD_PRIVATE (GtkBoxGadget))
58
59 static gboolean
60 gtk_box_gadget_child_is_visible (GObject *child)
61 {
62 if (GTK_IS_WIDGET (child))
63 return gtk_widget_get_visible (GTK_WIDGET (child));
64 else
65 return gtk_css_gadget_get_visible (GTK_CSS_GADGET (child));
66 }
67
68 static gboolean
gtk_box_gadget_child_compute_expand(GtkBoxGadget * gadget,GtkBoxGadgetChild * child)69 gtk_box_gadget_child_compute_expand (GtkBoxGadget *gadget,
70 GtkBoxGadgetChild *child)
71 {
72 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
73
74 if (child->expand)
75 return TRUE;
76
77 if (GTK_IS_WIDGET (child->object))
78 return gtk_widget_compute_expand (GTK_WIDGET (child->object), priv->orientation);
79
80 return FALSE;
81 }
82
83 static GtkAlign
gtk_box_gadget_child_get_align(GtkBoxGadget * gadget,GtkBoxGadgetChild * child)84 gtk_box_gadget_child_get_align (GtkBoxGadget *gadget,
85 GtkBoxGadgetChild *child)
86 {
87 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
88 GtkAlign align;
89
90 if (GTK_IS_WIDGET (child->object))
91 {
92 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
93 g_object_get (child->object, "valign", &align, NULL);
94 else
95 g_object_get (child->object, "halign", &align, NULL);
96 }
97 else
98 align = child->align;
99
100 return align;
101 }
102
103 static GtkAlign
effective_align(GtkAlign align,gboolean reverse)104 effective_align (GtkAlign align,
105 gboolean reverse)
106 {
107 switch (align)
108 {
109 case GTK_ALIGN_START:
110 return reverse ? GTK_ALIGN_END : GTK_ALIGN_START;
111 case GTK_ALIGN_END:
112 return reverse ? GTK_ALIGN_START : GTK_ALIGN_END;
113 default:
114 return align;
115 }
116 }
117
118 static void
gtk_box_gadget_measure_child(GObject * child,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)119 gtk_box_gadget_measure_child (GObject *child,
120 GtkOrientation orientation,
121 gint for_size,
122 gint *minimum,
123 gint *natural,
124 gint *minimum_baseline,
125 gint *natural_baseline)
126 {
127 if (GTK_IS_WIDGET (child))
128 {
129 _gtk_widget_get_preferred_size_for_size (GTK_WIDGET (child),
130 orientation,
131 for_size,
132 minimum, natural,
133 minimum_baseline, natural_baseline);
134 }
135 else
136 {
137 gtk_css_gadget_get_preferred_size (GTK_CSS_GADGET (child),
138 orientation,
139 for_size,
140 minimum, natural,
141 minimum_baseline, natural_baseline);
142 }
143 }
144
145 static void
gtk_box_gadget_distribute(GtkBoxGadget * gadget,gint for_size,gint size,GtkRequestedSize * sizes)146 gtk_box_gadget_distribute (GtkBoxGadget *gadget,
147 gint for_size,
148 gint size,
149 GtkRequestedSize *sizes)
150 {
151 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
152 guint i, n_expand;
153
154 n_expand = 0;
155
156 for (i = 0 ; i < priv->children->len; i++)
157 {
158 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
159
160 gtk_box_gadget_measure_child (child->object,
161 priv->orientation,
162 for_size,
163 &sizes[i].minimum_size, &sizes[i].natural_size,
164 NULL, NULL);
165 if (gtk_box_gadget_child_is_visible (child->object) &&
166 gtk_box_gadget_child_compute_expand (gadget, child))
167 n_expand++;
168 size -= sizes[i].minimum_size;
169 }
170
171 if G_UNLIKELY (size < 0)
172 {
173 g_critical ("%s: assertion 'size >= 0' failed in %s", G_STRFUNC, G_OBJECT_TYPE_NAME (gtk_css_gadget_get_owner (GTK_CSS_GADGET (gadget))));
174 return;
175 }
176
177 size = gtk_distribute_natural_allocation (size, priv->children->len, sizes);
178
179 if (size <= 0 || n_expand == 0)
180 return;
181
182 for (i = 0 ; i < priv->children->len; i++)
183 {
184 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
185
186 if (!gtk_box_gadget_child_is_visible (child->object) ||
187 !gtk_box_gadget_child_compute_expand (gadget, child))
188 continue;
189
190 sizes[i].minimum_size += size / n_expand;
191 /* distribute all pixels, even if there's a remainder */
192 size -= size / n_expand;
193 n_expand--;
194 }
195
196 }
197
198 static void
gtk_box_gadget_measure_orientation(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)199 gtk_box_gadget_measure_orientation (GtkCssGadget *gadget,
200 GtkOrientation orientation,
201 gint for_size,
202 gint *minimum,
203 gint *natural,
204 gint *minimum_baseline,
205 gint *natural_baseline)
206 {
207 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
208 gint child_min, child_nat;
209 guint i;
210
211 *minimum = 0;
212 *natural = 0;
213
214 for (i = 0 ; i < priv->children->len; i++)
215 {
216 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
217
218 gtk_box_gadget_measure_child (child->object,
219 orientation,
220 for_size,
221 &child_min, &child_nat,
222 NULL, NULL);
223
224 *minimum += child_min;
225 *natural += child_nat;
226 }
227 }
228
229 static void
gtk_box_gadget_measure_opposite(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)230 gtk_box_gadget_measure_opposite (GtkCssGadget *gadget,
231 GtkOrientation orientation,
232 gint for_size,
233 gint *minimum,
234 gint *natural,
235 gint *minimum_baseline,
236 gint *natural_baseline)
237 {
238 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
239 int child_min, child_nat, child_min_baseline, child_nat_baseline;
240 int total_min, above_min, below_min, total_nat, above_nat, below_nat;
241 GtkRequestedSize *sizes;
242 guint i;
243
244 if (for_size >= 0)
245 {
246 sizes = g_newa (GtkRequestedSize, priv->children->len);
247 gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), -1, for_size, sizes);
248 }
249
250 above_min = below_min = above_nat = below_nat = -1;
251 total_min = total_nat = 0;
252
253 for (i = 0 ; i < priv->children->len; i++)
254 {
255 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
256
257 gtk_box_gadget_measure_child (child->object,
258 orientation,
259 for_size >= 0 ? sizes[i].minimum_size : -1,
260 &child_min, &child_nat,
261 &child_min_baseline, &child_nat_baseline);
262
263 if (child_min_baseline >= 0)
264 {
265 below_min = MAX (below_min, child_min - child_min_baseline);
266 above_min = MAX (above_min, child_min_baseline);
267 below_nat = MAX (below_nat, child_nat - child_nat_baseline);
268 above_nat = MAX (above_nat, child_nat_baseline);
269 }
270 else
271 {
272 total_min = MAX (total_min, child_min);
273 total_nat = MAX (total_nat, child_nat);
274 }
275 }
276
277 if (above_min >= 0)
278 {
279 total_min = MAX (total_min, above_min + below_min);
280 total_nat = MAX (total_nat, above_nat + below_nat);
281 /* assume GTK_BASELINE_POSITION_CENTER for now */
282 if (minimum_baseline)
283 *minimum_baseline = above_min + (total_min - (above_min + below_min)) / 2;
284 if (natural_baseline)
285 *natural_baseline = above_nat + (total_nat - (above_nat + below_nat)) / 2;
286 }
287
288 *minimum = total_min;
289 *natural = total_nat;
290 }
291
292 static void
gtk_box_gadget_get_preferred_size(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)293 gtk_box_gadget_get_preferred_size (GtkCssGadget *gadget,
294 GtkOrientation orientation,
295 gint for_size,
296 gint *minimum,
297 gint *natural,
298 gint *minimum_baseline,
299 gint *natural_baseline)
300 {
301 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
302
303 if (priv->orientation == orientation)
304 gtk_box_gadget_measure_orientation (gadget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
305 else
306 gtk_box_gadget_measure_opposite (gadget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
307 }
308
309 static void
gtk_box_gadget_allocate_child(GObject * child,GtkOrientation box_orientation,GtkAlign child_align,GtkAllocation * allocation,int baseline,GtkAllocation * out_clip)310 gtk_box_gadget_allocate_child (GObject *child,
311 GtkOrientation box_orientation,
312 GtkAlign child_align,
313 GtkAllocation *allocation,
314 int baseline,
315 GtkAllocation *out_clip)
316 {
317 if (GTK_IS_WIDGET (child))
318 {
319 gtk_widget_size_allocate_with_baseline (GTK_WIDGET (child), allocation, baseline);
320 gtk_widget_get_clip (GTK_WIDGET (child), out_clip);
321 }
322 else
323 {
324 GtkAllocation child_allocation;
325 int minimum, natural;
326 int minimum_baseline, natural_baseline;
327
328 if (box_orientation == GTK_ORIENTATION_HORIZONTAL)
329 {
330 child_allocation.width = allocation->width;
331 child_allocation.x = allocation->x;
332
333 gtk_css_gadget_get_preferred_size (GTK_CSS_GADGET (child),
334 GTK_ORIENTATION_VERTICAL,
335 allocation->width,
336 &minimum, &natural,
337 &minimum_baseline, &natural_baseline);
338
339 switch (child_align)
340 {
341 case GTK_ALIGN_FILL:
342 child_allocation.height = allocation->height;
343 child_allocation.y = allocation->y;
344 break;
345 case GTK_ALIGN_START:
346 child_allocation.height = MIN(natural, allocation->height);
347 child_allocation.y = allocation->y;
348 break;
349 case GTK_ALIGN_END:
350 child_allocation.height = MIN(natural, allocation->height);
351 child_allocation.y = allocation->y + allocation->height - child_allocation.height;
352 break;
353 case GTK_ALIGN_BASELINE:
354 if (minimum_baseline >= 0 && baseline >= 0)
355 {
356 child_allocation.height = MIN(natural, allocation->height);
357 child_allocation.y = allocation->y + MAX(0, baseline - minimum_baseline);
358 break;
359 }
360 case GTK_ALIGN_CENTER:
361 child_allocation.height = MIN(natural, allocation->height);
362 child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
363 break;
364 default:
365 g_assert_not_reached ();
366 }
367 }
368 else
369 {
370 child_allocation.height = allocation->height;
371 child_allocation.y = allocation->y;
372
373 gtk_css_gadget_get_preferred_size (GTK_CSS_GADGET (child),
374 GTK_ORIENTATION_HORIZONTAL,
375 allocation->height,
376 &minimum, &natural,
377 NULL, NULL);
378
379 switch (child_align)
380 {
381 case GTK_ALIGN_FILL:
382 child_allocation.width = allocation->width;
383 child_allocation.x = allocation->x;
384 break;
385 case GTK_ALIGN_START:
386 child_allocation.width = MIN(natural, allocation->width);
387 child_allocation.x = allocation->x;
388 break;
389 case GTK_ALIGN_END:
390 child_allocation.width = MIN(natural, allocation->width);
391 child_allocation.x = allocation->x + allocation->width - child_allocation.width;
392 break;
393 case GTK_ALIGN_BASELINE:
394 case GTK_ALIGN_CENTER:
395 child_allocation.width = MIN(natural, allocation->width);
396 child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
397 break;
398 default:
399 g_assert_not_reached ();
400 }
401 }
402
403 gtk_css_gadget_allocate (GTK_CSS_GADGET (child), &child_allocation, baseline, out_clip);
404 }
405 }
406
407 static void
gtk_box_gadget_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip)408 gtk_box_gadget_allocate (GtkCssGadget *gadget,
409 const GtkAllocation *allocation,
410 int baseline,
411 GtkAllocation *out_clip)
412 {
413 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
414 GtkRequestedSize *sizes;
415 GtkAllocation child_allocation, child_clip;
416 GtkAlign child_align;
417 guint i;
418
419 child_allocation = *allocation;
420 sizes = g_newa (GtkRequestedSize, priv->children->len);
421
422 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
423 {
424 gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), allocation->height, allocation->width, sizes);
425
426 if (priv->allocate_reverse)
427 child_allocation.x = allocation->x + allocation->width;
428
429 for (i = 0; i < priv->children->len; i++)
430 {
431 guint idx = priv->allocate_reverse ? priv->children->len - 1 - i : i;
432 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, idx);
433 child_allocation.width = sizes[idx].minimum_size;
434 child_allocation.height = allocation->height;
435 child_allocation.y = allocation->y;
436 if (priv->allocate_reverse)
437 child_allocation.x -= child_allocation.width;
438
439 child_align = gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child);
440 gtk_box_gadget_allocate_child (child->object,
441 priv->orientation,
442 effective_align (child_align, priv->align_reverse),
443 &child_allocation,
444 baseline,
445 &child_clip);
446
447 if (i == 0)
448 *out_clip = child_clip;
449 else
450 gdk_rectangle_union (out_clip, &child_clip, out_clip);
451
452 if (!priv->allocate_reverse)
453 child_allocation.x += sizes[idx].minimum_size;
454 }
455 }
456 else
457 {
458 gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), allocation->width, allocation->height, sizes);
459
460 if (priv->allocate_reverse)
461 child_allocation.y = allocation->y + allocation->height;
462
463 for (i = 0 ; i < priv->children->len; i++)
464 {
465 guint idx = priv->allocate_reverse ? priv->children->len - 1 - i : i;
466 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, idx);
467 child_allocation.height = sizes[idx].minimum_size;
468 child_allocation.width = allocation->width;
469 child_allocation.x = allocation->x;
470 if (priv->allocate_reverse)
471 child_allocation.y -= child_allocation.height;
472
473 child_align = gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child);
474 gtk_box_gadget_allocate_child (child->object,
475 priv->orientation,
476 effective_align (child_align, priv->align_reverse),
477 &child_allocation,
478 -1,
479 &child_clip);
480
481 if (i == 0)
482 *out_clip = child_clip;
483 else
484 gdk_rectangle_union (out_clip, &child_clip, out_clip);
485
486 if (!priv->allocate_reverse)
487 child_allocation.y += sizes[idx].minimum_size;
488 }
489 }
490 }
491
492 static gboolean
gtk_box_gadget_draw(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height)493 gtk_box_gadget_draw (GtkCssGadget *gadget,
494 cairo_t *cr,
495 int x,
496 int y,
497 int width,
498 int height)
499 {
500 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
501 GtkWidget *owner = gtk_css_gadget_get_owner (gadget);
502 guint i;
503
504 for (i = 0; i < priv->children->len; i++)
505 {
506 guint draw_index = priv->draw_reverse ? priv->children->len - 1 - i : i;
507 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, draw_index);
508
509 if (GTK_IS_WIDGET (child->object))
510 gtk_container_propagate_draw (GTK_CONTAINER (owner), GTK_WIDGET (child->object), cr);
511 else
512 gtk_css_gadget_draw (GTK_CSS_GADGET (child->object), cr);
513 }
514
515 if (priv->draw_focus && gtk_widget_has_visible_focus (owner))
516 return TRUE;
517
518 return FALSE;
519 }
520
521 static void
gtk_box_gadget_finalize(GObject * object)522 gtk_box_gadget_finalize (GObject *object)
523 {
524 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (object));
525
526 g_array_free (priv->children, TRUE);
527
528 G_OBJECT_CLASS (gtk_box_gadget_parent_class)->finalize (object);
529 }
530
531 static void
gtk_box_gadget_class_init(GtkBoxGadgetClass * klass)532 gtk_box_gadget_class_init (GtkBoxGadgetClass *klass)
533 {
534 GtkCssGadgetClass *gadget_class = GTK_CSS_GADGET_CLASS (klass);
535 GObjectClass *object_class = G_OBJECT_CLASS (klass);
536
537 object_class->finalize = gtk_box_gadget_finalize;
538
539 gadget_class->get_preferred_size = gtk_box_gadget_get_preferred_size;
540 gadget_class->allocate = gtk_box_gadget_allocate;
541 gadget_class->draw = gtk_box_gadget_draw;
542 }
543
544 static void
gtk_box_gadget_clear_child(gpointer data)545 gtk_box_gadget_clear_child (gpointer data)
546 {
547 GtkBoxGadgetChild *child = data;
548
549 g_object_unref (child->object);
550 }
551
552 static void
gtk_box_gadget_init(GtkBoxGadget * gadget)553 gtk_box_gadget_init (GtkBoxGadget *gadget)
554 {
555 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
556
557 priv->children = g_array_new (FALSE, FALSE, sizeof (GtkBoxGadgetChild));
558 g_array_set_clear_func (priv->children, gtk_box_gadget_clear_child);
559 }
560
561 GtkCssGadget *
gtk_box_gadget_new_for_node(GtkCssNode * node,GtkWidget * owner)562 gtk_box_gadget_new_for_node (GtkCssNode *node,
563 GtkWidget *owner)
564 {
565 return g_object_new (GTK_TYPE_BOX_GADGET,
566 "node", node,
567 "owner", owner,
568 NULL);
569 }
570
571 GtkCssGadget *
gtk_box_gadget_new(const char * name,GtkWidget * owner,GtkCssGadget * parent,GtkCssGadget * next_sibling)572 gtk_box_gadget_new (const char *name,
573 GtkWidget *owner,
574 GtkCssGadget *parent,
575 GtkCssGadget *next_sibling)
576 {
577 GtkCssNode *node;
578 GtkCssGadget *result;
579
580 node = gtk_css_node_new ();
581 gtk_css_node_set_name (node, g_intern_string (name));
582 if (parent)
583 gtk_css_node_insert_before (gtk_css_gadget_get_node (parent),
584 node,
585 next_sibling ? gtk_css_gadget_get_node (next_sibling) : NULL);
586
587 result = gtk_box_gadget_new_for_node (node, owner);
588
589 g_object_unref (node);
590
591 return result;
592 }
593
594 void
gtk_box_gadget_set_orientation(GtkBoxGadget * gadget,GtkOrientation orientation)595 gtk_box_gadget_set_orientation (GtkBoxGadget *gadget,
596 GtkOrientation orientation)
597 {
598 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
599
600 priv->orientation = orientation;
601 }
602
603 void
gtk_box_gadget_set_draw_focus(GtkBoxGadget * gadget,gboolean draw_focus)604 gtk_box_gadget_set_draw_focus (GtkBoxGadget *gadget,
605 gboolean draw_focus)
606 {
607 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
608
609 priv->draw_focus = draw_focus;
610 }
611
612 void
gtk_box_gadget_set_draw_reverse(GtkBoxGadget * gadget,gboolean draw_reverse)613 gtk_box_gadget_set_draw_reverse (GtkBoxGadget *gadget,
614 gboolean draw_reverse)
615 {
616 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
617
618 priv->draw_reverse = draw_reverse;
619 }
620
621 void
gtk_box_gadget_set_allocate_reverse(GtkBoxGadget * gadget,gboolean allocate_reverse)622 gtk_box_gadget_set_allocate_reverse (GtkBoxGadget *gadget,
623 gboolean allocate_reverse)
624 {
625 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
626
627 priv->allocate_reverse = allocate_reverse;
628 }
629
630 void
gtk_box_gadget_set_align_reverse(GtkBoxGadget * gadget,gboolean align_reverse)631 gtk_box_gadget_set_align_reverse (GtkBoxGadget *gadget,
632 gboolean align_reverse)
633 {
634 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
635
636 priv->align_reverse = align_reverse;
637 }
638
639 static GtkCssNode *
get_css_node(GObject * child)640 get_css_node (GObject *child)
641 {
642 if (GTK_IS_WIDGET (child))
643 return gtk_widget_get_css_node (GTK_WIDGET (child));
644 else
645 return gtk_css_gadget_get_node (GTK_CSS_GADGET (child));
646 }
647
648 static void
gtk_box_gadget_insert_object(GtkBoxGadget * gadget,int pos,GObject * object,gboolean expand,GtkAlign align)649 gtk_box_gadget_insert_object (GtkBoxGadget *gadget,
650 int pos,
651 GObject *object,
652 gboolean expand,
653 GtkAlign align)
654 {
655 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
656 GtkBoxGadgetChild child;
657
658 child.object = g_object_ref (object);
659 child.expand = expand;
660 child.align = align;
661
662 if (pos < 0 || pos >= priv->children->len)
663 {
664 g_array_append_val (priv->children, child);
665 gtk_css_node_insert_before (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)),
666 get_css_node (object),
667 NULL);
668 }
669 else
670 {
671 g_array_insert_val (priv->children, pos, child);
672 gtk_css_node_insert_before (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)),
673 get_css_node (object),
674 get_css_node (g_array_index (priv->children, GtkBoxGadgetChild, pos + 1).object));
675 }
676 }
677
678 void
gtk_box_gadget_insert_widget(GtkBoxGadget * gadget,int pos,GtkWidget * widget)679 gtk_box_gadget_insert_widget (GtkBoxGadget *gadget,
680 int pos,
681 GtkWidget *widget)
682 {
683 gtk_box_gadget_insert_object (gadget, pos, G_OBJECT (widget), FALSE, GTK_ALIGN_FILL);
684 }
685
686 static GtkBoxGadgetChild *
gtk_box_gadget_find_object(GtkBoxGadget * gadget,GObject * object,int * position)687 gtk_box_gadget_find_object (GtkBoxGadget *gadget,
688 GObject *object,
689 int *position)
690 {
691 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
692 guint i;
693
694 for (i = 0; i < priv->children->len; i++)
695 {
696 GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
697
698 if (child->object == object)
699 {
700 if (position)
701 *position = i;
702 return child;
703 }
704 }
705
706 return NULL;
707 }
708
709 static void
gtk_box_gadget_remove_object(GtkBoxGadget * gadget,GObject * object)710 gtk_box_gadget_remove_object (GtkBoxGadget *gadget,
711 GObject *object)
712 {
713 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
714 GtkBoxGadgetChild *child;
715 int position;
716
717 child = gtk_box_gadget_find_object (gadget, object, &position);
718 if (child)
719 {
720 gtk_css_node_set_parent (get_css_node (child->object), NULL);
721 g_array_remove_index (priv->children, position);
722 }
723 }
724
725 void
gtk_box_gadget_remove_widget(GtkBoxGadget * gadget,GtkWidget * widget)726 gtk_box_gadget_remove_widget (GtkBoxGadget *gadget,
727 GtkWidget *widget)
728 {
729 gtk_box_gadget_remove_object (gadget, G_OBJECT (widget));
730 }
731
732 void
gtk_box_gadget_insert_gadget_before(GtkBoxGadget * gadget,GtkCssGadget * sibling,GtkCssGadget * cssgadget,gboolean expand,GtkAlign align)733 gtk_box_gadget_insert_gadget_before (GtkBoxGadget *gadget,
734 GtkCssGadget *sibling,
735 GtkCssGadget *cssgadget,
736 gboolean expand,
737 GtkAlign align)
738 {
739 /* Insert at the end if no sibling specified */
740 int pos = -1;
741
742 if (sibling)
743 gtk_box_gadget_find_object (gadget, G_OBJECT (sibling), &pos);
744
745 gtk_box_gadget_insert_gadget (gadget, pos, cssgadget, expand, align);
746 }
747
748 void
gtk_box_gadget_insert_gadget_after(GtkBoxGadget * gadget,GtkCssGadget * sibling,GtkCssGadget * cssgadget,gboolean expand,GtkAlign align)749 gtk_box_gadget_insert_gadget_after (GtkBoxGadget *gadget,
750 GtkCssGadget *sibling,
751 GtkCssGadget *cssgadget,
752 gboolean expand,
753 GtkAlign align)
754 {
755 /* Insert at the beginning if no sibling specified */
756 int pos = 0;
757
758 if (sibling && gtk_box_gadget_find_object (gadget, G_OBJECT (sibling), &pos))
759 pos++;
760
761 gtk_box_gadget_insert_gadget (gadget, pos, cssgadget, expand, align);
762 }
763
764 void
gtk_box_gadget_insert_gadget(GtkBoxGadget * gadget,int pos,GtkCssGadget * cssgadget,gboolean expand,GtkAlign align)765 gtk_box_gadget_insert_gadget (GtkBoxGadget *gadget,
766 int pos,
767 GtkCssGadget *cssgadget,
768 gboolean expand,
769 GtkAlign align)
770 {
771 gtk_box_gadget_insert_object (gadget, pos, G_OBJECT (cssgadget), expand, align);
772 }
773
774 void
gtk_box_gadget_remove_gadget(GtkBoxGadget * gadget,GtkCssGadget * cssgadget)775 gtk_box_gadget_remove_gadget (GtkBoxGadget *gadget,
776 GtkCssGadget *cssgadget)
777 {
778 gtk_box_gadget_remove_object (gadget, G_OBJECT (cssgadget));
779 }
780
781 void
gtk_box_gadget_reverse_children(GtkBoxGadget * gadget)782 gtk_box_gadget_reverse_children (GtkBoxGadget *gadget)
783 {
784 GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
785 int i, j;
786
787 gtk_css_node_reverse_children (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)));
788
789 for (i = 0, j = priv->children->len - 1; i < j; i++, j--)
790 {
791 GtkBoxGadgetChild *child1 = &g_array_index (priv->children, GtkBoxGadgetChild, i);
792 GtkBoxGadgetChild *child2 = &g_array_index (priv->children, GtkBoxGadgetChild, j);
793 GtkBoxGadgetChild tmp;
794
795 tmp = *child1;
796 *child1 = *child2;
797 *child2 = tmp;
798 }
799 }
800
801 void
gtk_box_gadget_set_gadget_expand(GtkBoxGadget * gadget,GObject * object,gboolean expand)802 gtk_box_gadget_set_gadget_expand (GtkBoxGadget *gadget,
803 GObject *object,
804 gboolean expand)
805 {
806 GtkBoxGadgetChild *child;
807
808 child = gtk_box_gadget_find_object (gadget, object, NULL);
809
810 if (!child)
811 return;
812
813 if (child->expand == expand)
814 return;
815
816 child->expand = expand;
817 gtk_css_gadget_queue_resize (GTK_CSS_GADGET (gadget));
818 }
819
820 void
gtk_box_gadget_set_gadget_align(GtkBoxGadget * gadget,GObject * object,GtkAlign align)821 gtk_box_gadget_set_gadget_align (GtkBoxGadget *gadget,
822 GObject *object,
823 GtkAlign align)
824 {
825 GtkBoxGadgetChild *child;
826
827 child = gtk_box_gadget_find_object (gadget, object, NULL);
828
829 if (!child)
830 return;
831
832 if (child->align == align)
833 return;
834
835 child->align = align;
836 gtk_css_gadget_queue_resize (GTK_CSS_GADGET (gadget));
837 }
838