[hyena/gtk3] ListView: prevent negative SizeAllocation calls from parent container



commit 0745bfb75809886925dfa49a57c79e5f71565d08
Author: Andrés G. Aragoneses <knocte gmail com>
Date:   Mon Aug 19 11:03:07 2013 +0200

    ListView: prevent negative SizeAllocation calls from parent container
    
    Apparently, GTK2 is much more protected (or GTK3 is more "liberal") in
    regards to invalid or weird combinations of GtkAdjustment values, as these
    didn't generate any rendering issues before. In GTK3 however, these values
    could cause the parent container to be confused and to send calls to
    gtk_widget_size_allocate[1] with negative coordinates.
    
    There were a lot of edge cases which were fixed in this commit:
    - hadjustment.Upper values becoming negative (fixed by preventing negative
    values in list_rendering_alloc.Width|Height).
    - PageSize and PageIncrement having positive values when Upper and Lower
    were zero.
    - vadjustment.Upper being higher than zero, but lower than
    vadjustment.PageSize.
    - vadjustment.Value becoming negative if there were not enough items in the
    listview to fill a whole page (the case when there is no need for
    scrollsbars).
    
    The last of the edge cases was actually highlighted to me thanks to a
    (presumably) accidental change that Bertrand Lorentz did in the way the
    hyena demo ("make hg") is rendered. I reverted this change here [2], but
    now I'm reinstating it in the form of two kinds of listviews for the demo:
    one which initially shows scrollbars and one that doesn't.
    
    This commit pretty much fixes the majority of annoyances and poltergeists
    I was seeing with the UI when running Banshee with its gtk3 branch of it
    and the gtk3 branch of hyena. Let's cross fingers to hope it doesn't appear
    again (but if it does, now we at least have some Log.Error() calls that I
    added in OnSizeAllocated() to warn us).
    
    I also added a nifty Dump() extension method for GtkAdjustment that is very
    handy when debugging these puppies.
    
    [1] https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-size-allocate
    [2] https://git.gnome.org/browse/hyena/commit/?h=gtk3&id=fe543a612804c37ba63e65bec813fa3ad3cf5658

 .../ListView/ListView_Interaction.cs               |   16 ++++++++++++-
 .../Hyena.Data.Gui/ListView/ListView_Windowing.cs  |   13 +++++++++--
 Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs     |   22 +++++++++++++++----
 Hyena.Gui/Hyena.Gui/GtkUtilities.cs                |   12 ++++++++++-
 4 files changed, 52 insertions(+), 11 deletions(-)
---
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs 
b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
index 5fd9ae3..f41480e 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Interaction.cs
@@ -6,10 +6,12 @@
 //   Gabriel Burt <gburt novell com>
 //   Eitan Isaacson <eitan ascender com>
 //   Alex Launi <alex launi canonical com>
+//   Andrés G. Aragoneses <knocte gmail com>
 //
 // Copyright (C) 2007-2009 Novell, Inc.
 // Copyright (C) 2009 Eitan Isaacson
 // Copyright (C) 2010 Alex Launi
+// Copyright (C) 2013 Andrés G. Aragoneses
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -1004,7 +1006,10 @@ namespace Hyena.Data.Gui
                 hadjustment.Upper = header_width;
                 hadjustment.StepIncrement = 10.0;
                 if (hadjustment.Value + hadjustment.PageSize > hadjustment.Upper) {
-                    hadjustment.Value = hadjustment.Upper - hadjustment.PageSize;
+                    hadjustment.Value = Math.Max (0, hadjustment.Upper - hadjustment.PageSize);
+                }
+                if (hadjustment.Upper > 0 && hadjustment.Upper < hadjustment.PageSize) {
+                    hadjustment.Upper = hadjustment.PageSize;
                 }
             }
 
@@ -1019,12 +1024,19 @@ namespace Hyena.Data.Gui
                 }
 
                 if (vadjustment.Value + vadjustment.PageSize > vadjustment.Upper) {
-                    vadjustment.Value = vadjustment.Upper - vadjustment.PageSize;
+                    vadjustment.Value = Math.Max (0, vadjustment.Upper - vadjustment.PageSize);
+                }
+                if (vadjustment.Upper > 0 && vadjustment.Upper < vadjustment.PageSize) {
+                    vadjustment.Upper = vadjustment.PageSize;
                 }
             } else if (vadjustment != null) {
                 // model is null
                 vadjustment.Upper = 0;
                 vadjustment.Lower = 0;
+                vadjustment.PageSize = 0;
+                vadjustment.PageIncrement = 0;
+                vadjustment.StepIncrement = 0;
+                vadjustment.Value = 0;
             }
 
             if (hadjustment != null) {
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs 
b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
index 594055b..4c42cb8 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Windowing.cs
@@ -126,9 +126,10 @@ namespace Hyena.Data.Gui
 
             list_rendering_alloc.X = header_rendering_alloc.X + Theme.TotalBorderWidth;
             list_rendering_alloc.Y = header_rendering_alloc.Bottom + Theme.TotalBorderWidth;
-            list_rendering_alloc.Width = allocation.Width - Theme.TotalBorderWidth * 2;
-            list_rendering_alloc.Height = allocation.Height - (list_rendering_alloc.Y - allocation.Y) -
-                Theme.TotalBorderWidth;
+            list_rendering_alloc.Width = Math.Max (0,
+                allocation.Width - Theme.TotalBorderWidth * 2);
+            list_rendering_alloc.Height = Math.Max (0,
+                allocation.Height - (list_rendering_alloc.Y - allocation.Y) - Theme.TotalBorderWidth);
 
             header_interaction_alloc = header_rendering_alloc;
             header_interaction_alloc.X = list_rendering_alloc.X;
@@ -166,6 +167,12 @@ namespace Hyena.Data.Gui
 
         protected override void OnSizeAllocated (Rectangle allocation)
         {
+            if (allocation.X < 0 || allocation.Y < 0) {
+                Log.Error ("SizeAllocate call received from container with negative coordinate(s): " + 
allocation.ToString () + Environment.NewLine +
+                           "(Prevent adjustments from receiving bad values; i.e.: negative values, or upper 
values lower than page size/increment, or any positive Value when the upper value is zero, etc.)");
+                return;
+            }
+
             base.OnSizeAllocated (allocation);
 
             if (IsRealized) {
diff --git a/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs b/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
index 62978b2..89ba89a 100644
--- a/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
+++ b/Hyena.Gui/Hyena.Data.Gui/ListViewTestModule.cs
@@ -40,13 +40,25 @@ using Selection = Hyena.Collections.Selection;
 
 namespace Hyena.Data.Gui.Tests
 {
+    [TestModule ("List View with scrollbars")]
+    public class ListViewWithScrollbarsTestModule : ListViewTestModule
+    {
+        public ListViewWithScrollbarsTestModule () : base (1000)
+        {
+        }
+    }
+
     [TestModule ("List View")]
     public class ListViewTestModule : Window
     {
         private View view;
         private Model model;
 
-        public ListViewTestModule () : base ("ListView")
+        public ListViewTestModule () : this (10)
+        {
+        }
+
+        public ListViewTestModule (int items) : base ("ListView")
         {
             WindowPosition = WindowPosition.Center;
             SetDefaultSize (800, 600);
@@ -56,7 +68,7 @@ namespace Hyena.Data.Gui.Tests
             scroll.VscrollbarPolicy = PolicyType.Automatic;
 
             view = new View ();
-            model = new Model ();
+            model = new Model (items);
 
             scroll.Add (view);
             Add (scroll);
@@ -95,7 +107,7 @@ namespace Hyena.Data.Gui.Tests
             SetDefaultSize (800, 600);
 
             view = new View ();
-            model = new Model ();
+            model = new Model (1000);
 
             /*var hbox = new HBox () { Spacing = 6 };
 
@@ -166,10 +178,10 @@ namespace Hyena.Data.Gui.Tests
         public event EventHandler Cleared;
         public event EventHandler Reloaded;
 
-        public Model ()
+        public Model (int items)
         {
             Random random = new Random (0);
-            for (int i = 0; i < 1000; i++) {
+            for (int i = 0; i < items; i++) {
                 store.Add (new ModelItem (i, random));
             }
         }
diff --git a/Hyena.Gui/Hyena.Gui/GtkUtilities.cs b/Hyena.Gui/Hyena.Gui/GtkUtilities.cs
index 6c89b2a..0fdc1d4 100644
--- a/Hyena.Gui/Hyena.Gui/GtkUtilities.cs
+++ b/Hyena.Gui/Hyena.Gui/GtkUtilities.cs
@@ -1,10 +1,12 @@
 //
 // GtkUtilities.cs
 //
-// Author:
+// Authors:
 //   Aaron Bockover <abockover novell com>
+//   Andrés G. Aragoneses <knocte gmail com>
 //
 // Copyright 2007-2010 Novell, Inc.
+// Copyright 2013 Andrés G. Aragoneses
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -142,6 +144,14 @@ namespace Hyena.Gui
             }
         }
 
+        internal static string Dump (this Gtk.Adjustment alig) {
+            if (alig == null) {
+                return "<null>";
+            }
+            return 
String.Format("Value:{0},PageSize{1},PageIncrement:{2},StepIncrement:{3},Lower:{4},Upper:{5}",
+                                 alig.Value, alig.PageSize, alig.PageIncrement, alig.StepIncrement, 
alig.Lower, alig.Upper);
+        }
+
         [Obsolete ("Use Gtk.Global.ShowUri() from gtk# 3.x")]
         public static bool ShowUri (string uri)
         {


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