[gtk+/native-layout: 29/31] GtkTreeview now watches the allocation of the parent scrolled window at allocation time in order to



commit 6d47681722fb823a8fb1494eb354b37974902b40
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Tue Jun 29 17:01:13 2010 -0400

    GtkTreeview now watches the allocation of the parent scrolled window
    at allocation time in order to detect possible feedback loops.
    
    To avoid locking up the desktop some feedback protection is needed,
    this patch is not the best but as far as I can see it does the job.
    Ideally we should be looking into a GtkScrollable interface that
    takes this height-for-width possibility into account.
    
    The problem being that some height-for-width calculations at
    allocation time are asynchronous and at some sizes cause the
    height-for-width logic to compete with parent allocations
    which change due to scrollbars toggling visiblity.
    
    Specific considerations
      a.) The child treeview should not have knowlage of its parents
          allocation or the size of the scrollbars (this patch cheats
          that idealistic rule).
      b.) The parent scrolled window cannot safely determine if a child
          wants its size renegotiated based on a content change or based
          on a previous allocation

 gtk/gtktreeprivate.h |   13 ++++++++
 gtk/gtktreeview.c    |   77 ++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 79 insertions(+), 11 deletions(-)
---
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
index 11b02e5..d1aa9df 100644
--- a/gtk/gtktreeprivate.h
+++ b/gtk/gtktreeprivate.h
@@ -101,6 +101,14 @@ struct _GtkTreeViewPrivate
   gint width;
   gint height;
 
+  /* Track parent scrolled window size to 
+   * avoid feed back loops with scrollbar allocations
+   * and h4w cell renderers in the layout.
+   */
+  gint prev_parent_width;
+  gint prev_parent_height;
+  gint consecutive_allocations;
+
   /* Adjustments */
   GtkAdjustment *hadjustment;
   GtkAdjustment *vadjustment;
@@ -300,6 +308,11 @@ struct _GtkTreeViewPrivate
 
   /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
   guint search_entry_avoid_unhandled_binding : 1;
+
+  /* Mark dirty state for resizes that originate from changes in
+   * the full rendered content size, from resizes that originate
+   * from */
+  guint content_size_dirty : 1;
 };
 
 struct _GtkTreeViewColumnPrivate
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 4a2b23b..a04f161 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -45,6 +45,7 @@
 #include "gtkframe.h"
 #include "gtktreemodelsort.h"
 #include "gtktooltip.h"
+#include "gtkscrolledwindow.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
 
@@ -299,7 +300,8 @@ static gboolean do_validate_rows         (GtkTreeView *tree_view,
 					  gboolean     size_request);
 static gboolean validate_rows            (GtkTreeView *tree_view);
 static gboolean presize_handler_callback (gpointer     data);
-static void     install_presize_handler  (GtkTreeView *tree_view);
+static void     install_presize_handler  (GtkTreeView *tree_view,
+					  gboolean     content_dirty);
 static void     install_scroll_sync_handler (GtkTreeView *tree_view);
 static void     gtk_tree_view_set_top_row   (GtkTreeView *tree_view,
 					     GtkTreePath *path,
@@ -1877,7 +1879,7 @@ gtk_tree_view_realize (GtkWidget *widget)
   gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
   gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
 
-  install_presize_handler (tree_view); 
+  install_presize_handler (tree_view, TRUE); 
 }
 
 static void
@@ -2384,6 +2386,7 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
   GList *tmp_list;
   gboolean width_changed = FALSE;
   gint old_width = widget->allocation.width;
+  gboolean scroll_window_feedback = FALSE;
 
   if (allocation->width != widget->allocation.width)
     width_changed = TRUE;
@@ -2407,11 +2410,49 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
       gtk_widget_size_allocate (child->widget, &allocation);
     }
 
+  /* If we get various width allocations while our content did not change
+   * and the parent is a scrolled window and it's own allocation has not
+   * changed:
+   *
+   * Stop setting the adjustments until
+   *  a.) Treeview content size changes
+   *  b.) Parent's allocation changes
+   */
+  if (GTK_IS_SCROLLED_WINDOW (gtk_widget_get_parent (widget)))
+    {
+      GtkWidget *swindow = gtk_widget_get_parent (widget);
+      GtkAllocation salloc;
+      
+      gtk_widget_get_allocation (swindow, &salloc);
+      
+      if (!tree_view->priv->content_size_dirty &&
+	  tree_view->priv->prev_parent_width == salloc.width &&
+	  tree_view->priv->prev_parent_height == salloc.height)
+	{
+	  /* Feedback detected */
+	  if (tree_view->priv->consecutive_allocations >= 3)
+	    scroll_window_feedback = TRUE;
+	  else
+	    tree_view->priv->consecutive_allocations++;
+	}
+      else
+	tree_view->priv->consecutive_allocations = 0;
+      
+      tree_view->priv->content_size_dirty = FALSE;
+      tree_view->priv->prev_parent_width  = salloc.width;
+      tree_view->priv->prev_parent_height = salloc.height;
+    }
+  else
+    tree_view->priv->consecutive_allocations = 0;
+  
+
   /* We size-allocate the columns first because the width of the
    * tree view (used in updating the adjustments below) might change.
    */
   gtk_tree_view_size_allocate_columns (widget, &width_changed);
 
+  if (!scroll_window_feedback)
+    {
   tree_view->priv->hadjustment->page_size = allocation->width;
   tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
   tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
@@ -2453,6 +2494,7 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
   tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
 
   gtk_adjustment_changed (tree_view->priv->vadjustment);
+    }
 
   /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
   if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
@@ -2550,7 +2592,7 @@ gtk_tree_view_size_allocate (GtkWidget     *widget,
 	    _gtk_tree_view_column_cell_set_dirty (column, FALSE, FALSE);
 	}
       
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, FALSE);
       
       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
     }
@@ -6494,8 +6536,12 @@ presize_handler_callback (gpointer data)
 }
 
 static void
-install_presize_handler (GtkTreeView *tree_view)
+install_presize_handler (GtkTreeView *tree_view,
+			 gboolean     content_dirty)
 {
+  if (content_dirty)
+    tree_view->priv->content_size_dirty = TRUE;
+
   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
     return;
 
@@ -6655,7 +6701,7 @@ _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view)
 {
   tree_view->priv->mark_rows_col_dirty = TRUE;
 
-  install_presize_handler (tree_view);
+  install_presize_handler (tree_view, FALSE);
 }
 
 /*
@@ -7800,7 +7846,7 @@ gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
       tree_view->priv->fixed_height = -1;
 
       /* force a revalidation */
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, TRUE);
     }
   else 
     {
@@ -7822,6 +7868,8 @@ gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
       
       if (tree_view->priv->tree)
 	initialize_fixed_height_mode (tree_view);
+
+      tree_view->priv->content_size_dirty = TRUE;
     }
 
   g_object_notify (G_OBJECT (tree_view), "fixed-height-mode");
@@ -8421,9 +8469,12 @@ gtk_tree_view_row_changed (GtkTreeModel *model,
  done:
   if (!tree_view->priv->fixed_height_mode &&
       gtk_widget_get_realized (GTK_WIDGET (tree_view)))
-    install_presize_handler (tree_view);
+    install_presize_handler (tree_view, TRUE);
+
   if (free_path)
     gtk_tree_path_free (path);
+
+  tree_view->priv->content_size_dirty = TRUE;
 }
 
 static void
@@ -8536,9 +8587,11 @@ gtk_tree_view_row_inserted (GtkTreeModel *model,
 	gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
     }
   else
-    install_presize_handler (tree_view);
+    install_presize_handler (tree_view, TRUE);
   if (free_path)
     gtk_tree_path_free (path);
+
+  tree_view->priv->content_size_dirty = TRUE;
 }
 
 static void
@@ -10924,7 +10977,7 @@ gtk_tree_view_set_model (GtkTreeView  *tree_view,
       gtk_tree_path_free (path);
 
       /*  FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, TRUE);
     }
 
   g_object_notify (G_OBJECT (tree_view), "model");
@@ -10934,6 +10987,8 @@ gtk_tree_view_set_model (GtkTreeView  *tree_view,
 
   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
+
+  tree_view->priv->content_size_dirty = TRUE;
 }
 
 /**
@@ -11793,7 +11848,7 @@ gtk_tree_view_scroll_to_cell (GtkTreeView       *tree_view,
       tree_view->priv->scroll_to_row_align = row_align;
       tree_view->priv->scroll_to_col_align = col_align;
 
-      install_presize_handler (tree_view);
+      install_presize_handler (tree_view, FALSE);
     }
   else
     {
@@ -12206,7 +12261,7 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
   if (animate)
     add_expand_collapse_timeout (tree_view, tree, node, TRUE);
 
-  install_presize_handler (tree_view);
+  install_presize_handler (tree_view, TRUE);
 
   g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
   if (open_all && node->children)



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