3 * This file is part of gtk
5 * Copyright (C) 2011 - Ignacio Casal Quinteiro, Mike Krüger
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 #include "gtkoverlay.h"
24 #include "gtkbuildable.h"
25 #include "gtkscrolledwindow.h"
26 #include "gtkmarshalers.h"
28 #include "gtkprivate.h"
33 * @short_description: A container which overlays widgets on top of each other
36 * GtkOverlay is a container which contains a single main child, on top
37 * of which it can place <firstterm>overlay</firstterm> widgets. The
38 * position of each overlay widget is determined by its #GtkWidget:halign
39 * and #GtkWidget:valign properties. E.g. a widget with both alignments
40 * set to %GTK_ALIGN_START will be placed at the top left corner of the
41 * main widget, whereas an overlay with halign set to %GTK_ALIGN_CENTER
42 * and valign set to %GTK_ALIGN_END will be placed a the bottom edge of
43 * the main widget, horizontally centered. The position can be adjusted
44 * by setting the margin properties of the child to non-zero values.
46 * More complicated placement of overlays is possible by connecting
47 * to the #GtkOverlay::get-child-position signal.
49 * <refsect2 id="GtkOverlay-BUILDER-UI">
50 * <title>GtkOverlay as GtkBuildable</title>
52 * The GtkOverlay implementation of the GtkBuildable interface
53 * supports placing a child as an overlay by specifying "overlay" as
54 * the "type" attribute of a <tag class="starttag">child</tag> element.
59 struct _GtkOverlayPrivate
64 typedef struct _GtkOverlayChild GtkOverlayChild;
66 struct _GtkOverlayChild
77 static guint signals[LAST_SIGNAL] = { 0 };
79 static void gtk_overlay_buildable_init (GtkBuildableIface *iface);
81 G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN,
82 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
83 gtk_overlay_buildable_init))
86 gtk_overlay_create_child_window (GtkOverlay *overlay,
89 GtkWidget *widget = GTK_WIDGET (overlay);
90 GtkAllocation allocation;
92 GdkWindowAttr attributes;
95 gtk_widget_get_allocation (child, &allocation);
97 attributes.window_type = GDK_WINDOW_CHILD;
98 attributes.wclass = GDK_INPUT_OUTPUT;
99 attributes.width = allocation.width;
100 attributes.height = allocation.height;
101 attributes.x = allocation.x;
102 attributes.y = allocation.y;
103 attributes_mask = GDK_WA_X | GDK_WA_Y;
104 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
106 window = gdk_window_new (gtk_widget_get_window (widget),
107 &attributes, attributes_mask);
108 gdk_window_set_user_data (window, overlay);
109 gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
111 gtk_widget_set_parent_window (child, window);
117 gtk_overlay_child_allocate (GtkOverlay *overlay,
118 GtkOverlayChild *child)
120 gint left, right, top, bottom;
121 GtkAllocation allocation, child_allocation, overlay_allocation;
124 if (gtk_widget_get_mapped (GTK_WIDGET (overlay)))
126 if (gtk_widget_get_visible (child->widget))
127 gdk_window_show (child->window);
128 else if (gdk_window_is_visible (child->window))
129 gdk_window_hide (child->window);
132 if (!gtk_widget_get_visible (child->widget))
135 g_signal_emit (overlay, signals[GET_CHILD_POSITION],
136 0, child->widget, &allocation, &result);
138 gtk_widget_get_allocation (GTK_WIDGET (overlay), &overlay_allocation);
140 allocation.x += overlay_allocation.x;
141 allocation.y += overlay_allocation.y;
143 /* put the margins outside the window; also arrange things
144 * so that the adjusted child allocation still ends up at 0, 0
146 left = gtk_widget_get_margin_left (child->widget);
147 right = gtk_widget_get_margin_right (child->widget);
148 top = gtk_widget_get_margin_top (child->widget);
149 bottom = gtk_widget_get_margin_bottom (child->widget);
151 child_allocation.x = - left;
152 child_allocation.y = - top;
153 child_allocation.width = allocation.width;
154 child_allocation.height = allocation.height;
156 allocation.x += left;
158 allocation.width -= left + right;
159 allocation.height -= top + bottom;
162 gdk_window_move_resize (child->window,
163 allocation.x, allocation.y,
164 allocation.width, allocation.height);
166 gtk_widget_size_allocate (child->widget, &child_allocation);
170 gtk_overlay_get_preferred_width (GtkWidget *widget,
174 GtkBin *bin = GTK_BIN (widget);
183 child = gtk_bin_get_child (bin);
184 if (child && gtk_widget_get_visible (child))
185 gtk_widget_get_preferred_width (child, minimum, natural);
189 gtk_overlay_get_preferred_height (GtkWidget *widget,
193 GtkBin *bin = GTK_BIN (widget);
202 child = gtk_bin_get_child (bin);
203 if (child && gtk_widget_get_visible (child))
204 gtk_widget_get_preferred_height (child, minimum, natural);
208 gtk_overlay_size_allocate (GtkWidget *widget,
209 GtkAllocation *allocation)
211 GtkOverlay *overlay = GTK_OVERLAY (widget);
212 GtkOverlayPrivate *priv = overlay->priv;
214 GtkWidget *main_widget;
216 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->size_allocate (widget, allocation);
218 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
219 if (!main_widget || !gtk_widget_get_visible (main_widget))
222 gtk_widget_size_allocate (main_widget, allocation);
224 for (children = priv->children; children; children = children->next)
226 gtk_overlay_child_allocate (overlay, children->data);
231 effective_align (GtkAlign align,
232 GtkTextDirection direction)
236 case GTK_ALIGN_START:
237 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START;
239 return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END;
246 gtk_overlay_get_child_position (GtkOverlay *overlay,
248 GtkAllocation *alloc)
250 GtkWidget *main_widget;
251 GtkAllocation main_alloc;
254 GtkTextDirection direction;
256 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
258 /* special-case scrolled windows */
259 if (GTK_IS_SCROLLED_WINDOW (main_widget))
261 GtkWidget *grandchild;
264 grandchild = gtk_bin_get_child (GTK_BIN (main_widget));
265 gtk_widget_translate_coordinates (grandchild, main_widget, 0, 0, &x, &y);
269 main_alloc.width = gtk_widget_get_allocated_width (grandchild);
270 main_alloc.height = gtk_widget_get_allocated_height (grandchild);
276 main_alloc.width = gtk_widget_get_allocated_width (main_widget);
277 main_alloc.height = gtk_widget_get_allocated_height (main_widget);
280 gtk_widget_get_preferred_size (widget, NULL, &req);
282 alloc->x = main_alloc.x;
283 alloc->width = MIN (main_alloc.width, req.width);
285 direction = gtk_widget_get_direction (widget);
287 halign = gtk_widget_get_halign (widget);
288 switch (effective_align (halign, direction))
290 case GTK_ALIGN_START:
294 alloc->width = main_alloc.width;
296 case GTK_ALIGN_CENTER:
297 alloc->x += main_alloc.width / 2 - req.width / 2;
300 alloc->x += main_alloc.width - req.width;
304 alloc->y = main_alloc.y;
305 alloc->height = MIN (main_alloc.height, req.height);
307 switch (gtk_widget_get_valign (widget))
309 case GTK_ALIGN_START:
313 alloc->height = main_alloc.height;
315 case GTK_ALIGN_CENTER:
316 alloc->y += main_alloc.height / 2 - req.height / 2;
319 alloc->y += main_alloc.height - req.height;
327 gtk_overlay_realize (GtkWidget *widget)
329 GtkOverlay *overlay = GTK_OVERLAY (widget);
330 GtkOverlayPrivate *priv = overlay->priv;
331 GtkOverlayChild *child;
334 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->realize (widget);
336 for (children = priv->children; children; children = children->next)
338 child = children->data;
340 if (child->window == NULL)
341 child->window = gtk_overlay_create_child_window (overlay, child->widget);
346 gtk_overlay_unrealize (GtkWidget *widget)
348 GtkOverlay *overlay = GTK_OVERLAY (widget);
349 GtkOverlayPrivate *priv = overlay->priv;
350 GtkOverlayChild *child;
353 for (children = priv->children; children; children = children->next)
355 child = children->data;
357 gtk_widget_set_parent_window (child->widget, NULL);
358 gdk_window_set_user_data (child->window, NULL);
359 gdk_window_destroy (child->window);
360 child->window = NULL;
363 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unrealize (widget);
367 gtk_overlay_map (GtkWidget *widget)
369 GtkOverlay *overlay = GTK_OVERLAY (widget);
370 GtkOverlayPrivate *priv = overlay->priv;
371 GtkOverlayChild *child;
374 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->map (widget);
376 for (children = priv->children; children; children = children->next)
378 child = children->data;
380 if (child->window != NULL &&
381 gtk_widget_get_visible (child->widget) &&
382 gtk_widget_get_child_visible (child->widget))
383 gdk_window_show (child->window);
388 gtk_overlay_unmap (GtkWidget *widget)
390 GtkOverlay *overlay = GTK_OVERLAY (widget);
391 GtkOverlayPrivate *priv = overlay->priv;
392 GtkOverlayChild *child;
395 for (children = priv->children; children; children = children->next)
397 child = children->data;
399 if (child->window != NULL &&
400 gdk_window_is_visible (child->window))
401 gdk_window_hide (child->window);
404 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->unmap (widget);
408 gtk_overlay_draw (GtkWidget *widget,
411 GtkOverlay *overlay = GTK_OVERLAY (widget);
412 GtkOverlayPrivate *priv = overlay->priv;
413 GtkOverlayChild *child;
416 for (children = priv->children; children; children = children->next)
418 child = children->data;
420 if (gtk_cairo_should_draw_window (cr, child->window))
423 gtk_cairo_transform_to_window (cr, widget, child->window);
424 gtk_render_background (gtk_widget_get_style_context (widget),
427 gdk_window_get_width (child->window),
428 gdk_window_get_height (child->window));
433 GTK_WIDGET_CLASS (gtk_overlay_parent_class)->draw (widget, cr);
439 gtk_overlay_remove (GtkContainer *container,
442 GtkOverlayPrivate *priv = GTK_OVERLAY (container)->priv;
443 GtkOverlayChild *child;
446 for (children = priv->children; children; children = children->next)
448 child = children->data;
450 if (child->widget == widget)
452 if (child->window != NULL)
454 gdk_window_set_user_data (child->window, NULL);
455 gdk_window_destroy (child->window);
458 gtk_widget_unparent (widget);
460 priv->children = g_slist_delete_link (priv->children, children);
461 g_slice_free (GtkOverlayChild, child);
467 GTK_CONTAINER_CLASS (gtk_overlay_parent_class)->remove (container, widget);
471 gtk_overlay_forall (GtkContainer *overlay,
472 gboolean include_internals,
473 GtkCallback callback,
474 gpointer callback_data)
476 GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
477 GtkOverlayChild *child;
479 GtkWidget *main_widget;
481 main_widget = gtk_bin_get_child (GTK_BIN (overlay));
483 (* callback) (main_widget, callback_data);
485 children = priv->children;
488 child = children->data;
489 children = children->next;
491 (* callback) (child->widget, callback_data);
496 gtk_overlay_class_init (GtkOverlayClass *klass)
498 GObjectClass *object_class = G_OBJECT_CLASS (klass);
499 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
500 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
502 widget_class->get_preferred_width = gtk_overlay_get_preferred_width;
503 widget_class->get_preferred_height = gtk_overlay_get_preferred_height;
504 widget_class->size_allocate = gtk_overlay_size_allocate;
505 widget_class->realize = gtk_overlay_realize;
506 widget_class->unrealize = gtk_overlay_unrealize;
507 widget_class->map = gtk_overlay_map;
508 widget_class->unmap = gtk_overlay_unmap;
509 widget_class->draw = gtk_overlay_draw;
511 container_class->remove = gtk_overlay_remove;
512 container_class->forall = gtk_overlay_forall;
514 klass->get_child_position = gtk_overlay_get_child_position;
517 * GtkOverlay::get-child-position:
518 * @overlay: the #GtkOverlay
519 * @widget: the child widget to position
520 * @allocation: (out): return location for the allocation
522 * The ::get-child-position signal is emitted to determine
523 * the position and size of any overlay child widgets. A
524 * handler for this signal should fill @allocation with
525 * the desired position and size for @widget, relative to
526 * the 'main' child of @overlay.
528 * The default handler for this signal uses the @widget's
529 * halign and valign properties to determine the position
530 * and gives the widget its natural size (except that an
531 * alignment of %GTK_ALIGN_FILL will cause the overlay to
532 * be full-width/height). If the main child is a
533 * #GtkScrolledWindow, the overlays are placed relative
536 * Return: %TRUE if the @allocation has been filled
538 signals[GET_CHILD_POSITION] =
539 g_signal_new (I_("get-child-position"),
540 G_TYPE_FROM_CLASS (object_class),
542 G_STRUCT_OFFSET (GtkOverlayClass, get_child_position),
543 _gtk_boolean_handled_accumulator, NULL,
544 _gtk_marshal_BOOLEAN__OBJECT_BOXED,
547 GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
549 g_type_class_add_private (object_class, sizeof (GtkOverlayPrivate));
553 gtk_overlay_init (GtkOverlay *overlay)
555 overlay->priv = G_TYPE_INSTANCE_GET_PRIVATE (overlay, GTK_TYPE_OVERLAY, GtkOverlayPrivate);
557 gtk_widget_set_has_window (GTK_WIDGET (overlay), FALSE);
561 gtk_overlay_buildable_add_child (GtkBuildable *buildable,
566 if (type && strcmp (type, "overlay") == 0)
567 gtk_overlay_add_overlay (GTK_OVERLAY (buildable), GTK_WIDGET (child));
569 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
571 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
575 gtk_overlay_buildable_init (GtkBuildableIface *iface)
577 iface->add_child = gtk_overlay_buildable_add_child;
583 * Creates a new #GtkOverlay.
585 * Returns: a new #GtkOverlay object.
590 gtk_overlay_new (void)
592 return g_object_new (GTK_TYPE_OVERLAY, NULL);
596 * gtk_overlay_add_overlay:
597 * @overlay: a #GtkOverlay
598 * @widget: a #GtkWidget to be added to the container
600 * Adds @widget to @overlay.
602 * The widget will be stacked on top of the main widget
603 * added with gtk_container_add().
605 * The position at which @widget is placed is determined
606 * from its #GtkWidget:halign and #GtkWidget:valign properties.
611 gtk_overlay_add_overlay (GtkOverlay *overlay,
614 GtkOverlayPrivate *priv = overlay->priv;
615 GtkOverlayChild *child;
617 g_return_if_fail (GTK_IS_OVERLAY (overlay));
618 g_return_if_fail (GTK_IS_WIDGET (widget));
620 child = g_slice_new0 (GtkOverlayChild);
621 child->widget = widget;
623 priv->children = g_slist_append (priv->children, child);
625 if (gtk_widget_get_realized (GTK_WIDGET (overlay)))
627 child->window = gtk_overlay_create_child_window (overlay, widget);
628 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
629 gtk_overlay_child_allocate (overlay, child);
632 gtk_widget_set_parent (widget, GTK_WIDGET (overlay));