Browse Source

Merge pull request #5655 from sdoroff/grid-sharedsizegroup-layoutloop-bug

Fixed SharedSizeGroup Layout Bug
Max Katz 4 years ago
parent
commit
ae7af6c0fd
1 changed files with 55 additions and 22 deletions
  1. 55 22
      src/Avalonia.Controls/DefinitionBase.cs

+ 55 - 22
src/Avalonia.Controls/DefinitionBase.cs

@@ -662,31 +662,64 @@ namespace Avalonia.Controls
                 {
                 {
                     DefinitionBase definitionBase = _registry[i];
                     DefinitionBase definitionBase = _registry[i];
 
 
-                    if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
+                    // we'll set d.UseSharedMinimum to maintain the invariant:
+                    //      d.UseSharedMinimum iff d._minSize < this.MinSize
+                    // i.e. iff d is not a "long-pole" definition.
+                    //
+                    // Measure/Arrange of d's Grid uses d._minSize for long-pole
+                    // definitions, and max(d._minSize, shared size) for
+                    // short-pole definitions.  This distinction allows us to react
+                    // to changes in "long-pole-ness" more efficiently and correctly,
+                    // by avoiding remeasures when a long-pole definition changes.
+                    bool useSharedMinimum = !MathUtilities.AreClose(definitionBase._minSize, sharedMinSize);
+
+                    // before doing that, determine whether d's Grid needs to be remeasured.
+                    // It's important _not_ to remeasure if the last measure is still
+                    // valid, otherwise infinite loops are possible
+                    bool measureIsValid;
+
+                    if(!definitionBase.UseSharedMinimum)
                     {
                     {
-                        //  if definition's min size is different, then need to re-measure
-                        if (!MathUtilities.AreClose(sharedMinSize, definitionBase.MinSize))
-                        {
-                            Grid parentGrid = (Grid)definitionBase.Parent;
-                            parentGrid.InvalidateMeasure();
-                            definitionBase.UseSharedMinimum = true;
-                        }
-                        else
-                        {
-                            definitionBase.UseSharedMinimum = false;
-
-                            //  if measure is valid then also need to check arrange.
-                            //  Note: definitionBase.SizeCache is volatile but at this point 
-                            //  it contains up-to-date final size
-                            if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
-                            {
-                                Grid parentGrid = (Grid)definitionBase.Parent;
-                                parentGrid.InvalidateArrange();
-                            }
-                        }
+                        // d was a long-pole.  measure is valid iff it's still a long-pole,
+                        // since previous measure didn't use shared size.
+                        measureIsValid = !useSharedMinimum;
+                    }
+                    else if(useSharedMinimum)
+                    {
+                        // d was a short-pole, and still is.  measure is valid
+                        // iff the shared size didn't change
+                        measureIsValid = !sharedMinSizeChanged;
+                    }
+                    else
+                    {
+                        // d was a short-pole, but is now a long-pole.  This can
+                        // happen in several ways:
+                        //  a. d's minSize increased to or past the old shared size
+                        //  b. other long-pole definitions decreased, leaving
+                        //      d as the new winner
+                        // In the former case, the measure is valid - it used
+                        // d's new larger minSize.  In the latter case, the
+                        // measure is invalid - it used the old shared size,
+                        // which is larger than d's (possibly changed) minSize
+                        measureIsValid = (definitionBase.LayoutWasUpdated &&
+                                        MathUtilities.GreaterThanOrClose(definitionBase._minSize, this.MinSize));
+                    }
 
 
-                        definitionBase.LayoutWasUpdated = false;
+                    if(!measureIsValid)
+                    {
+                        definitionBase.Parent.InvalidateMeasure();
                     }
                     }
+                    else if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
+                    {
+                        //  if measure is valid then also need to check arrange.
+                        //  Note: definitionBase.SizeCache is volatile but at this point 
+                        //  it contains up-to-date final size
+                        definitionBase.Parent.InvalidateArrange();
+                    }
+
+                    // now we can restore the invariant, and clear the layout flag
+                    definitionBase.UseSharedMinimum = useSharedMinimum;
+                    definitionBase.LayoutWasUpdated = false;
                 }
                 }
 
 
                 _minSize = sharedMinSize;
                 _minSize = sharedMinSize;