gtk+ r20818 - in trunk: . gtk tests



Author: kristian
Date: Fri Jul 11 14:17:49 2008
New Revision: 20818
URL: http://svn.gnome.org/viewvc/gtk+?rev=20818&view=rev

Log:
Fix #316087.

2008-07-11  Kristian Rietveld  <kris gtk org>

	Bug 316087 - Resizing columns is chaotic

	* gtk/gtktreeprivate.h: add new member fields.

	* gtk/gtktreeview.c (gtk_tree_view_init), (validate_row): set post
	validation flag,
	(gtk_tree_view_size_allocate_columns): rework the size allocation
	mechanism to only recalculate the expand values if the width of the
	widget, content or the column configuration has changed,
	(gtk_tree_view_size_allocate): move call to size_allocate_columns()
	to before the adjustment updates so the proper width is used after
	we updated it,
	(gtk_tree_view_button_press), (gtk_tree_view_motion_resize_column):
	use the column width minus the expand value for the resized width,
	(gtk_tree_view_move_column_after): update call to
	gtk_tree_view_size_allocate_columns().

	* gtk/gtktreeviewcolumn.c (gtk_tree_view_column_set_expand): set use
	resized width to FALSE.

	* tests/Makefile.am:
	* tests/testtreecolumnsizing.c: new interactive test program
	for testing column resizing with different column configurations.



Added:
   trunk/tests/testtreecolumnsizing.c
Modified:
   trunk/ChangeLog
   trunk/gtk/gtktreeprivate.h
   trunk/gtk/gtktreeview.c
   trunk/gtk/gtktreeviewcolumn.c
   trunk/tests/Makefile.am

Modified: trunk/gtk/gtktreeprivate.h
==============================================================================
--- trunk/gtk/gtktreeprivate.h	(original)
+++ trunk/gtk/gtktreeprivate.h	Fri Jul 11 14:17:49 2008
@@ -236,6 +236,8 @@
 
   guint in_grab : 1;
 
+  guint post_validation_flag : 1;
+
 
   /* Auto expand/collapse timeout in hover mode */
   guint auto_expand_timeout;
@@ -268,6 +270,10 @@
   GdkGC *tree_line_gc;
 
   gint tooltip_column;
+
+  gint last_extra_space;
+  gint last_extra_space_per_column;
+  gint last_number_of_expand_columns;
 };
 
 #ifdef __GNUC__

Modified: trunk/gtk/gtktreeview.c
==============================================================================
--- trunk/gtk/gtktreeview.c	(original)
+++ trunk/gtk/gtktreeview.c	Fri Jul 11 14:17:49 2008
@@ -1370,6 +1370,8 @@
   tree_view->priv->tree_lines_enabled = FALSE;
 
   tree_view->priv->tooltip_column = -1;
+
+  tree_view->priv->post_validation_flag = FALSE;
 }
 
 
@@ -1988,6 +1990,7 @@
 
   tree_view->priv->prev_width = tree_view->priv->width;  
   tree_view->priv->width = 0;
+
   /* keep this in sync with size_allocate below */
   for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
     {
@@ -2150,18 +2153,20 @@
 
 /* GtkWidget::size_allocate helper */
 static void
-gtk_tree_view_size_allocate_columns (GtkWidget *widget)
+gtk_tree_view_size_allocate_columns (GtkWidget *widget,
+				     gboolean  *width_changed)
 {
   GtkTreeView *tree_view;
   GList *list, *first_column, *last_column;
   GtkTreeViewColumn *column;
   GtkAllocation allocation;
   gint width = 0;
-  gint extra, extra_per_column;
+  gint extra, extra_per_column, extra_for_last;
   gint full_requested_width = 0;
   gint number_of_expand_columns = 0;
   gboolean column_changed = FALSE;
   gboolean rtl;
+  gboolean update_expand;
   
   tree_view = GTK_TREE_VIEW (widget);
 
@@ -2196,12 +2201,42 @@
 	number_of_expand_columns++;
     }
 
-  extra = MAX (widget->allocation.width - full_requested_width, 0);
+  /* Only update the expand value if the width of the widget has changed,
+   * or the number of expand columns has changed, or if there are no expand
+   * columns, or if we didn't have an size-allocation yet after the
+   * last validated node.
+   */
+  update_expand = (width_changed && *width_changed == TRUE)
+      || number_of_expand_columns != tree_view->priv->last_number_of_expand_columns
+      || number_of_expand_columns == 0
+      || tree_view->priv->post_validation_flag == TRUE;
+
+  tree_view->priv->post_validation_flag = FALSE;
+
+  if (!update_expand)
+    {
+      extra = tree_view->priv->last_extra_space;
+      extra_for_last = MAX (widget->allocation.width - full_requested_width - extra, 0);
+    }
+  else
+    {
+      extra = MAX (widget->allocation.width - full_requested_width, 0);
+      extra_for_last = 0;
+
+      tree_view->priv->last_extra_space = extra;
+    }
+
   if (number_of_expand_columns > 0)
     extra_per_column = extra/number_of_expand_columns;
   else
     extra_per_column = 0;
 
+  if (update_expand)
+    {
+      tree_view->priv->last_extra_space_per_column = extra_per_column;
+      tree_view->priv->last_number_of_expand_columns = number_of_expand_columns;
+    }
+
   for (list = (rtl ? last_column : first_column); 
        list != (rtl ? first_column->prev : last_column->next);
        list = (rtl ? list->prev : list->next)) 
@@ -2257,6 +2292,12 @@
 	  column->width += extra;
 	}
 
+      /* In addition to expand, the last column can get even more
+       * extra space so all available space is filled up.
+       */
+      if (extra_for_last > 0 && list == last_column)
+	column->width += extra_for_last;
+
       g_object_notify (G_OBJECT (column), "width");
 
       allocation.width = column->width;
@@ -2274,6 +2315,12 @@
                                 TREE_VIEW_DRAG_WIDTH, allocation.height);
     }
 
+  /* We change the width here.  The user might have been resizing columns,
+   * so the total width of the tree view changes.
+   */
+  tree_view->priv->width = width;
+  *width_changed = TRUE;
+
   if (column_changed)
     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
 }
@@ -2310,6 +2357,10 @@
       gtk_widget_size_allocate (child->widget, &allocation);
     }
 
+  /* 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);
 
   tree_view->priv->hadjustment->page_size = allocation->width;
   tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
@@ -2318,28 +2369,30 @@
   tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width);
 
   if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)   
-     {
+    {
       if (allocation->width < tree_view->priv->width)
-         {
-         if (tree_view->priv->init_hadjust_value)
-           {
-           tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
-           tree_view->priv->init_hadjust_value = FALSE;
-           }
-         else if(allocation->width != old_width)
-           tree_view->priv->hadjustment->value = CLAMP(tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
-         else
-           tree_view->priv->hadjustment->value = CLAMP(tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width);
-         }
-      else
-         {
-         tree_view->priv->hadjustment->value = 0;
-         tree_view->priv->init_hadjust_value = TRUE;
-         }
-     }
+        {
+	  if (tree_view->priv->init_hadjust_value)
+	    {
+	      tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
+	      tree_view->priv->init_hadjust_value = FALSE;
+	    }
+	  else if (allocation->width != old_width)
+	    {
+	      tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
+	    }
+	  else
+	    tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width);
+	}
+      else
+        {
+	  tree_view->priv->hadjustment->value = 0;
+	  tree_view->priv->init_hadjust_value = TRUE;
+	}
+    }
   else
-     if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
-        tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
+    if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
+      tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
 
   gtk_adjustment_changed (tree_view->priv->hadjustment);
 
@@ -2379,8 +2432,6 @@
 			      allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
     }
 
-  gtk_tree_view_size_allocate_columns (widget);
-
   if (tree_view->priv->tree == NULL)
     invalidate_empty_focus (tree_view);
 
@@ -2820,7 +2871,7 @@
 
 	  gtk_grab_add (widget);
 	  GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
-	  column->resized_width = column->width;
+	  column->resized_width = column->width - tree_view->priv->last_extra_space_per_column;
 
 	  /* block attached dnd signal handler */
 	  drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
@@ -3524,6 +3575,8 @@
     {
       column->use_resized_width = TRUE;
       column->resized_width = new_width;
+      if (column->expand)
+	column->resized_width -= tree_view->priv->last_extra_space_per_column;
       gtk_widget_queue_resize (widget);
     }
 
@@ -5619,6 +5672,7 @@
       _gtk_rbtree_node_set_height (tree, node, height);
     }
   _gtk_rbtree_node_mark_valid (tree, node);
+  tree_view->priv->post_validation_flag = TRUE;
 
   return retval;
 }
@@ -11408,7 +11462,7 @@
   if (GTK_WIDGET_REALIZED (tree_view))
     {
       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
-      gtk_tree_view_size_allocate_columns (GTK_WIDGET (tree_view));
+      gtk_tree_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
     }
 
   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);

Modified: trunk/gtk/gtktreeviewcolumn.c
==============================================================================
--- trunk/gtk/gtktreeviewcolumn.c	(original)
+++ trunk/gtk/gtktreeviewcolumn.c	Fri Jul 11 14:17:49 2008
@@ -2141,6 +2141,13 @@
       tree_column->tree_view != NULL &&
       GTK_WIDGET_REALIZED (tree_column->tree_view))
     {
+      /* We want to continue using the original width of the
+       * column that includes additional space added by the user
+       * resizing the columns and possibly extra (expanded) space, which
+       * are not included in the resized width.
+       */
+      tree_column->use_resized_width = FALSE;
+
       gtk_widget_queue_resize (tree_column->tree_view);
     }
 

Modified: trunk/tests/Makefile.am
==============================================================================
--- trunk/tests/Makefile.am	(original)
+++ trunk/tests/Makefile.am	Fri Jul 11 14:17:49 2008
@@ -72,6 +72,7 @@
 	testtreefocus			\
 	testtreeflow			\
 	testtreecolumns			\
+	testtreecolumnsizing		\
 	testtreesort			\
 	treestoretest			\
 	testxinerama			\
@@ -145,6 +146,7 @@
 testtreefocus_DEPENDENCIES = $(DEPS)
 testtreeflow_DEPENDENCIES = $(DEPS)
 testtreecolumns_DEPENDENCIES = $(DEPS)
+testtreecolumnsizing_DEPENDENCIES = $(DEPS)
 testtreesort_DEPENDENCIES = $(DEPS)
 treestoretest_DEPENDENCIES = $(TEST_DEPS)
 testxinerama_DEPENDENCIES = $(TEST_DEPS)
@@ -200,6 +202,7 @@
 testtreefocus_LDADD = $(LDADDS)
 testtreeflow_LDADD = $(LDADDS)
 testtreecolumns_LDADD = $(LDADDS)
+testtreecolumnsizing_LDADD = $(LDADDS)
 testtreesort_LDADD = $(LDADDS)
 testtext_LDADD = $(LDADDS)
 treestoretest_LDADD = $(LDADDS)

Added: trunk/tests/testtreecolumnsizing.c
==============================================================================
--- (empty file)
+++ trunk/tests/testtreecolumnsizing.c	Fri Jul 11 14:17:49 2008
@@ -0,0 +1,237 @@
+/* testtreecolumnsizing.c: Test case for tree view column resizing.
+ *
+ * Copyright (C) 2008  Kristian Rietveld  <kris gtk org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define NO_EXPAND "No expandable columns"
+#define SINGLE_EXPAND "One expandable column"
+#define MULTI_EXPAND "Multiple expandable columns"
+#define LAST_EXPAND "Last column is expandable"
+#define BORDER_EXPAND "First and last columns are expandable"
+#define ALL_EXPAND "All columns are expandable"
+
+#define N_ROWS 10
+
+
+static GtkTreeModel *
+create_model (void)
+{
+  int i;
+  GtkListStore *store;
+
+  store = gtk_list_store_new (5,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING);
+
+  for (i = 0; i < N_ROWS; i++)
+    {
+      gchar *str;
+
+      str = g_strdup_printf ("Row %d", i);
+      gtk_list_store_insert_with_values (store, NULL, i,
+                                         0, str,
+                                         1, "Blah blah blah blah blah",
+                                         2, "Less blah",
+                                         3, "Medium length",
+                                         4, "Eek",
+                                         -1);
+      g_free (str);
+    }
+
+  return GTK_TREE_MODEL (store);
+}
+
+static void
+toggle_long_content_row (GtkToggleButton *button,
+                         gpointer         user_data)
+{
+  GtkTreeModel *model;
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (user_data));
+  if (gtk_tree_model_iter_n_children (model, NULL) == N_ROWS)
+    {
+      gtk_list_store_insert_with_values (GTK_LIST_STORE (model), NULL, N_ROWS,
+                                         0, "Very very very very longggggg",
+                                         1, "Blah blah blah blah blah",
+                                         2, "Less blah",
+                                         3, "Medium length",
+                                         4, "Eek we make the scrollbar appear",
+                                         -1);
+    }
+  else
+    {
+      GtkTreeIter iter;
+
+      gtk_tree_model_iter_nth_child (model, &iter, NULL, N_ROWS);
+      gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+    }
+}
+
+static void
+combo_box_changed (GtkComboBox *combo_box,
+                   gpointer     user_data)
+{
+  gchar *str;
+  GList *list;
+  GList *columns;
+
+  str = gtk_combo_box_get_active_text (GTK_COMBO_BOX (combo_box));
+  if (!str)
+    return;
+
+  columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (user_data));
+
+  if (!strcmp (str, NO_EXPAND))
+    {
+      for (list = columns; list; list = list->next)
+        gtk_tree_view_column_set_expand (list->data, FALSE);
+    }
+  else if (!strcmp (str, SINGLE_EXPAND))
+    {
+      for (list = columns; list; list = list->next)
+        {
+          if (list->prev && !list->prev->prev)
+            /* This is the second column */
+            gtk_tree_view_column_set_expand (list->data, TRUE);
+          else
+            gtk_tree_view_column_set_expand (list->data, FALSE);
+        }
+    }
+  else if (!strcmp (str, MULTI_EXPAND))
+    {
+      for (list = columns; list; list = list->next)
+        {
+          if (list->prev && !list->prev->prev)
+            /* This is the second column */
+            gtk_tree_view_column_set_expand (list->data, TRUE);
+          else if (list->prev && !list->prev->prev->prev)
+            /* This is the third column */
+            gtk_tree_view_column_set_expand (list->data, TRUE);
+          else
+            gtk_tree_view_column_set_expand (list->data, FALSE);
+        }
+    }
+  else if (!strcmp (str, LAST_EXPAND))
+    {
+      for (list = columns; list->next; list = list->next)
+        gtk_tree_view_column_set_expand (list->data, FALSE);
+      /* This is the last column */
+      gtk_tree_view_column_set_expand (list->data, TRUE);
+    }
+  else if (!strcmp (str, BORDER_EXPAND))
+    {
+      gtk_tree_view_column_set_expand (columns->data, TRUE);
+      for (list = columns->next; list->next; list = list->next)
+        gtk_tree_view_column_set_expand (list->data, FALSE);
+      /* This is the last column */
+      gtk_tree_view_column_set_expand (list->data, TRUE);
+    }
+  else if (!strcmp (str, ALL_EXPAND))
+    {
+      for (list = columns; list; list = list->next)
+        gtk_tree_view_column_set_expand (list->data, TRUE);
+    }
+
+  g_free (str);
+  g_list_free (columns);
+}
+
+int
+main (int argc, char **argv)
+{
+  int i;
+  GtkWidget *window;
+  GtkWidget *vbox;
+  GtkWidget *combo_box;
+  GtkWidget *sw;
+  GtkWidget *tree_view;
+  GtkWidget *button;
+
+  gtk_init (&argc, &argv);
+
+  /* Window and box */
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
+  g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);
+  gtk_container_set_border_width (GTK_CONTAINER (window), 5);
+
+  vbox = gtk_vbox_new (FALSE, 5);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  /* Option menu contents */
+  combo_box = gtk_combo_box_new_text ();
+
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), NO_EXPAND);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), SINGLE_EXPAND);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), MULTI_EXPAND);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), LAST_EXPAND);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), BORDER_EXPAND);
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), ALL_EXPAND);
+
+  gtk_box_pack_start (GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
+
+  /* Scrolled window and tree view */
+  sw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+  gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+
+  tree_view = gtk_tree_view_new_with_model (create_model ());
+  gtk_container_add (GTK_CONTAINER (sw), tree_view);
+
+  for (i = 0; i < 5; i++)
+    {
+      GtkTreeViewColumn *column;
+
+      gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
+                                                   i, "Header",
+                                                   gtk_cell_renderer_text_new (),
+                                                   "text", i,
+                                                   NULL);
+
+      column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), i);
+      gtk_tree_view_column_set_resizable (column, TRUE);
+    }
+
+  /* Toggle button for long content row */
+  button = gtk_toggle_button_new_with_label ("Toggle long content row");
+  g_signal_connect (button, "toggled",
+                    G_CALLBACK (toggle_long_content_row), tree_view);
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  /* Set up option menu callback and default item */
+  g_signal_connect (combo_box, "changed",
+                    G_CALLBACK (combo_box_changed), tree_view);
+  gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
+
+  /* Done */
+  gtk_widget_show_all (window);
+
+  gtk_main ();
+
+  return 0;
+}



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