[gtk+/toplevel-embedding: 9/12] Fixed GtkWindow/GtkWidget to properly emit hierarchy changed for embedded toplevels



commit f62f13cefaee68a8b8ec1c15b1a97a6f111e4f37
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Sun Dec 26 19:08:33 2010 +0900

    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 3a2c0b8..d90f987 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -3719,14 +3719,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
@@ -3745,11 +3737,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
@@ -3773,12 +3767,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 f52da2d..0068ea9 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -9232,6 +9232,7 @@ _gtk_window_set_is_toplevel (GtkWindow *window,
 {
   GtkWidget *widget;
   GtkWidget *toplevel;
+  gboolean   was_anchored;
 
   widget = GTK_WIDGET (window);
 
@@ -9243,16 +9244,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
     {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]