overlay: Add reorder_overlay()

This allows you to control the z-ordering of overlay children

https://bugzilla.gnome.org/show_bug.cgi?id=750568

https://bugs.freedesktop.org/show_bug.cgi?id=90917
This commit is contained in:
Alexander Larsson 2015-06-08 16:28:32 +02:00
parent 4c3eece663
commit 76ba5a03b9
2 changed files with 203 additions and 1 deletions

View File

@ -71,6 +71,12 @@ enum {
LAST_SIGNAL
};
enum
{
CHILD_PROP_0,
CHILD_PROP_INDEX
};
static guint signals[LAST_SIGNAL] = { 0 };
static void gtk_overlay_buildable_init (GtkBuildableIface *iface);
@ -293,6 +299,9 @@ gtk_overlay_child_allocate (GtkOverlay *overlay,
if (gtk_widget_get_mapped (GTK_WIDGET (overlay)))
{
/* Note: This calls show every size allocation, which makes
* us keep the z-order of the chilren, as gdk_window_show()
* does an implicit raise. */
if (gtk_widget_get_visible (child->widget))
gdk_window_show (child->window);
else if (gdk_window_is_visible (child->window))
@ -485,7 +494,9 @@ gtk_overlay_remove (GtkContainer *container,
GtkOverlayPrivate *priv = GTK_OVERLAY (container)->priv;
GtkOverlayChild *child;
GSList *children;
gboolean removed;
removed = FALSE;
for (children = priv->children; children; children = children->next)
{
child = children->data;
@ -503,13 +514,94 @@ gtk_overlay_remove (GtkContainer *container,
priv->children = g_slist_delete_link (priv->children, children);
g_slice_free (GtkOverlayChild, child);
return;
removed = TRUE;
}
else if (removed)
gtk_widget_child_notify (child->widget, "index");
}
GTK_CONTAINER_CLASS (gtk_overlay_parent_class)->remove (container, widget);
}
/**
* gtk_overlay_reorder_overlay:
* @overlay: a #GtkOverlay
* @child: the overlaid #GtkWidget to move
* @index: the new index for @child in the list of overlay children
* of @overlay, starting from 0. If negative, indicates the end of
* the list
*
* Moves @child to a new @index in the list of @overlay children.
* The list contains overlays in the order that these were
* added to @overlay.
*
* A widgets index in the @overlay children list determines which order
* the children are drawn if they overlap. The first child is drawn at
* the bottom. It also affects the default focus chain order.
*/
void
gtk_overlay_reorder_overlay (GtkOverlay *overlay,
GtkWidget *child,
gint index)
{
GtkOverlayPrivate *priv;
GSList *old_link;
GSList *new_link;
GSList *l;
GtkOverlayChild *child_info = NULL;
gint old_index, i;
g_return_if_fail (GTK_IS_OVERLAY (overlay));
g_return_if_fail (GTK_IS_WIDGET (child));
priv = GTK_OVERLAY (overlay)->priv;
old_link = priv->children;
old_index = 0;
while (old_link)
{
child_info = old_link->data;
if (child_info->widget == child)
break;
old_link = old_link->next;
old_index++;
}
g_return_if_fail (old_link != NULL);
if (index < 0)
{
new_link = NULL;
index = g_slist_length (priv->children) - 1;
}
else
{
new_link = g_slist_nth (priv->children, index);
index = MIN (index, g_slist_length (priv->children) - 1);
}
if (index == old_index)
return;
priv->children = g_slist_delete_link (priv->children, old_link);
priv->children = g_slist_insert_before (priv->children, new_link, child_info);
for (i = 0, l = priv->children; l != NULL; l = l->next, i++)
{
GtkOverlayChild *info = l->data;
if ((i < index && i < old_index) ||
(i > index && i > old_index))
continue;
gtk_widget_child_notify (info->widget, "index");
}
if (gtk_widget_get_visible (child) &&
gtk_widget_get_visible (GTK_WIDGET (overlay)))
gtk_widget_queue_resize (GTK_WIDGET (overlay));
}
static void
gtk_overlay_forall (GtkContainer *overlay,
gboolean include_internals,
@ -535,6 +627,101 @@ gtk_overlay_forall (GtkContainer *overlay,
}
}
static GtkOverlayChild *
gtk_overlay_get_overlay_child (GtkOverlay *overlay,
GtkWidget *child)
{
GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
GtkOverlayChild *child_info;
GSList *children;
for (children = priv->children; children; children = children->next)
{
child_info = children->data;
if (child_info->widget == child)
return child_info;
}
return NULL;
}
static void
gtk_overlay_set_child_property (GtkContainer *container,
GtkWidget *child,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkOverlay *overlay = GTK_OVERLAY (container);
GtkOverlayChild *child_info;
GtkWidget *main_widget;
main_widget = gtk_bin_get_child (GTK_BIN (overlay));
if (child == main_widget)
child_info = NULL;
else
{
child_info = gtk_overlay_get_overlay_child (overlay, child);
if (child_info == NULL)
{
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
return;
}
}
switch (property_id)
{
case CHILD_PROP_INDEX:
if (child_info != NULL)
gtk_overlay_reorder_overlay (GTK_OVERLAY (container),
child,
g_value_get_int (value));
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
}
}
static void
gtk_overlay_get_child_property (GtkContainer *container,
GtkWidget *child,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkOverlay *overlay = GTK_OVERLAY (container);
GtkOverlayPrivate *priv = GTK_OVERLAY (overlay)->priv;
GtkOverlayChild *child_info;
GtkWidget *main_widget;
main_widget = gtk_bin_get_child (GTK_BIN (overlay));
if (child == main_widget)
child_info = NULL;
else
{
child_info = gtk_overlay_get_overlay_child (overlay, child);
if (child_info == NULL)
{
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
return;
}
}
switch (property_id)
{
case CHILD_PROP_INDEX:
g_value_set_int (value, g_slist_index (priv->children, child_info));
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
break;
}
}
static void
gtk_overlay_class_init (GtkOverlayClass *klass)
{
@ -550,9 +737,18 @@ gtk_overlay_class_init (GtkOverlayClass *klass)
container_class->remove = gtk_overlay_remove;
container_class->forall = gtk_overlay_forall;
container_class->set_child_property = gtk_overlay_set_child_property;
container_class->get_child_property = gtk_overlay_get_child_property;
klass->get_child_position = gtk_overlay_get_child_position;
gtk_container_class_install_child_property (container_class, CHILD_PROP_INDEX,
g_param_spec_int ("index",
P_("Index"),
P_("The index of the overlay in the parent, -1 for the main child"),
-1, G_MAXINT, 0,
GTK_PARAM_READWRITE));
/**
* GtkOverlay::get-child-position:
* @overlay: the #GtkOverlay
@ -669,4 +865,5 @@ gtk_overlay_add_overlay (GtkOverlay *overlay,
else
gtk_widget_set_parent (widget, GTK_WIDGET (overlay));
gtk_widget_child_notify (widget, "index");
}

View File

@ -83,6 +83,11 @@ GtkWidget *gtk_overlay_new (void);
GDK_AVAILABLE_IN_3_2
void gtk_overlay_add_overlay (GtkOverlay *overlay,
GtkWidget *widget);
GDK_AVAILABLE_IN_3_18
void gtk_overlay_reorder_overlay (GtkOverlay *overlay,
GtkWidget *child,
gint position);
G_END_DECLS