From 93c8058582aa8e6823bcf83b654da29f816a1547 Mon Sep 17 00:00:00 2001 From: Tristan Van Berkom Date: Sun, 26 Dec 2010 19:08:33 +0900 Subject: [PATCH] Fixed GtkWindow/GtkWidget to properly emit hierarchy changed for embedded toplevels Now GtkWindow takes some measures when setting toplevelness: - When a window becomes toplevel after being embedded it saves the visibility state and reshow's itself so that the window re-realizes and presents itself again automatically - When emitting hierarchy-changed, synthetically mark the toplevel as not anchored, this allows the hierarchy changed propagation to recurse properly. GtkWidget also takes care to unset the parent window *after* unparenting the widget and after emitting the heirarhcy changed that leaves a NULL toplevel. That means there are now 2 cycles of "hierarchy-changed" when removing an embedded toplevel from a parent, first one that makes the new toplevel a NULL one (since the toplevel flag is not yet restored), the second cycle makes the removed window toplevel again when setting the parent window to NULL. --- gtk/gtkwidget.c | 27 ++++++++++++++------------- gtk/gtkwindow.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 4ee58865ac..b740d1c13d 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -3721,14 +3721,6 @@ gtk_widget_unparent (GtkWidget *widget) if (gtk_container_get_focus_child (GTK_CONTAINER (priv->parent)) == widget) gtk_container_set_focus_child (GTK_CONTAINER (priv->parent), NULL); - /* If we are unanchoring the child, we save around the toplevel - * to emit hierarchy changed - */ - if (priv->parent->priv->anchored) - g_object_ref (toplevel); - else - toplevel = NULL; - gtk_widget_queue_draw_child (widget); /* Reset the width and height here, to force reallocation if we @@ -3747,11 +3739,13 @@ gtk_widget_unparent (GtkWidget *widget) gtk_widget_unrealize (widget); } - /* Need to unset the parent window early, this can result in - * an additional "hierarchy-changed" propagation if we are removing - * a parented GtkWindow from the hierarchy. + /* If we are unanchoring the child, we save around the toplevel + * to emit hierarchy changed */ - gtk_widget_set_parent_window (widget, NULL); + if (priv->parent->priv->anchored) + g_object_ref (toplevel); + else + toplevel = NULL; /* Removing a widget from a container restores the child visible * flag to the default state, so it doesn't affect the child @@ -3775,12 +3769,19 @@ gtk_widget_unparent (GtkWidget *widget) } g_signal_emit (widget, widget_signals[PARENT_SET], 0, old_parent); - if (toplevel && gtk_widget_is_toplevel (toplevel)) + if (toplevel) { _gtk_widget_propagate_hierarchy_changed (widget, toplevel); g_object_unref (toplevel); } + /* Now that the parent pointer is nullified and the hierarchy-changed + * already passed, go ahead and unset the parent window, if we are unparenting + * an embeded GtkWindow the window will become toplevel again and hierarchy-changed + * will fire again for the new subhierarchy. + */ + gtk_widget_set_parent_window (widget, NULL); + g_object_notify (G_OBJECT (widget), "parent"); g_object_thaw_notify (G_OBJECT (widget)); if (!priv->parent) diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 963e221edd..69ccde5bf9 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -9231,6 +9231,7 @@ _gtk_window_set_is_toplevel (GtkWindow *window, { GtkWidget *widget; GtkWidget *toplevel; + gboolean was_anchored; widget = GTK_WIDGET (window); @@ -9242,16 +9243,47 @@ _gtk_window_set_is_toplevel (GtkWindow *window, if (is_toplevel == gtk_widget_is_toplevel (widget)) return; + was_anchored = _gtk_widget_get_anchored (widget); + if (is_toplevel) { + gboolean was_visible = gtk_widget_get_visible (widget); + + /* Pass through regular pathways of an embedded toplevel + * to go through unmapping and hiding the widget before + * becomming a toplevel again. + */ + if (was_visible) + gtk_widget_hide (widget); + + /* Save the toplevel this widget was previously anchored into before + * propagating a hierarchy-changed. + * + * Usually this happens by way of gtk_widget_unparent() and we are + * already unanchored at this point, just adding this clause incase + * things happen differently. + */ toplevel = gtk_widget_get_toplevel (widget); - if (!gtk_widget_is_toplevel (toplevel)) + if (!gtk_widget_is_toplevel (widget)) toplevel = NULL; + _gtk_widget_set_is_toplevel (widget, TRUE); + + /* When a window becomes toplevel after being embedded and anchored + * into another window we need to unset it's anchored flag so that + * the hierarchy changed signal kicks in properly. + */ + _gtk_widget_set_anchored (widget, FALSE); _gtk_widget_propagate_hierarchy_changed (widget, toplevel); - _gtk_widget_set_is_toplevel (widget, TRUE); toplevel_list = g_slist_prepend (toplevel_list, window); + + /* If an embedded toplevel gets removed from the hierarchy + * and is still in a visible state, we need to show it again + * so it will be realized as a real toplevel again. + */ + if (was_visible) + gtk_widget_show (widget); } else {