diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index dcb3ef4a2b..2f138d9e83 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
+
namespace Avalonia.Utilities
{
///
@@ -8,6 +10,127 @@ namespace Avalonia.Utilities
///
public static class MathUtilities
{
+ ///
+ /// AreClose - Returns whether or not two doubles are "close". That is, whether or
+ /// not they are within epsilon of each other. Note that this epsilon is proportional
+ /// to the numbers themselves to that AreClose survives scalar multiplication.
+ /// There are plenty of ways for this to return false even for numbers which
+ /// are theoretically identical, so no code calling this should fail to work if this
+ /// returns false. This is important enough to repeat:
+ /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
+ /// used for optimizations *only*.
+ ///
+ ///
+ /// bool - the result of the AreClose comparision.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool AreClose(double value1, double value2)
+ {
+ //in case they are Infinities (then epsilon check does not work)
+ if (value1 == value2) return true;
+ double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * double.Epsilon;
+ double delta = value1 - value2;
+ return (-eps < delta) && (eps > delta);
+ }
+
+ ///
+ /// LessThan - Returns whether or not the first double is less than the second double.
+ /// That is, whether or not the first is strictly less than *and* not within epsilon of
+ /// the other number. Note that this epsilon is proportional to the numbers themselves
+ /// to that AreClose survives scalar multiplication. Note,
+ /// There are plenty of ways for this to return false even for numbers which
+ /// are theoretically identical, so no code calling this should fail to work if this
+ /// returns false. This is important enough to repeat:
+ /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
+ /// used for optimizations *only*.
+ ///
+ ///
+ /// bool - the result of the LessThan comparision.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool LessThan(double value1, double value2)
+ {
+ return (value1 < value2) && !AreClose(value1, value2);
+ }
+
+
+ ///
+ /// GreaterThan - Returns whether or not the first double is greater than the second double.
+ /// That is, whether or not the first is strictly greater than *and* not within epsilon of
+ /// the other number. Note that this epsilon is proportional to the numbers themselves
+ /// to that AreClose survives scalar multiplication.
+ ///
+ /// - the result of the GreaterThan comparision.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool GreaterThan(double value1, double value2)
+ {
+ return (value1 > value2) && !AreClose(value1, value2);
+ }
+
+ ///
+ /// LessThanOrClose - Returns whether or not the first double is less than or close to
+ /// the second double. That is, whether or not the first is strictly less than or within
+ /// epsilon of the other number. Note that this epsilon is proportional to the numbers
+ /// themselves to that AreClose survives scalar multiplication. Note,
+ /// There are plenty of ways for this to return false even for numbers which
+ /// are theoretically identical, so no code calling this should fail to work if this
+ /// returns false. This is important enough to repeat:
+ /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
+ /// used for optimizations *only*.
+ ///
+ ///
+ /// bool - the result of the LessThanOrClose comparision.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ public static bool LessThanOrClose(double value1, double value2)
+ {
+ return (value1 < value2) || AreClose(value1, value2);
+ }
+
+ ///
+ /// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
+ /// the second double. That is, whether or not the first is strictly greater than or within
+ /// epsilon of the other number.
+ ///
+ /// The first double to compare.
+ /// The second double to compare.
+ /// the result of the GreaterThanOrClose comparision.
+ public static bool GreaterThanOrClose(double value1, double value2)
+ {
+ return (value1 > value2) || AreClose(value1, value2);
+ }
+
+ ///
+ /// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
+ /// but this is faster.
+ ///
+ ///
+ /// bool - the result of the AreClose comparision.
+ ///
+ /// The double to compare to 1.
+ public static bool IsOne(double value)
+ {
+ return Math.Abs(value - 1.0) < 10.0 * double.Epsilon;
+ }
+
+ ///
+ /// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
+ /// but this is faster.
+ ///
+ ///
+ /// bool - the result of the AreClose comparision.
+ ///
+ /// The double to compare to 0.
+ public static bool IsZero(double value)
+ {
+ return Math.Abs(value) < 10.0 * double.Epsilon;
+ }
+
///
/// Clamps a value between a minimum and maximum value.
///
diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs
index 5726356830..05cb9dfb28 100644
--- a/src/Avalonia.Controls/DefinitionBase.cs
+++ b/src/Avalonia.Controls/DefinitionBase.cs
@@ -1,18 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
namespace Avalonia.Controls
{
+
///
/// Base class for and .
///
public class DefinitionBase : AvaloniaObject
{
+
///
/// Defines the property.
///
public static readonly StyledProperty SharedSizeGroupProperty =
- AvaloniaProperty.Register(nameof(SharedSizeGroup), inherits: true);
+ AvaloniaProperty.Register(nameof(SharedSizeGroup), inherits: true, validate: SharedSizeGroupPropertyValueValid);
///
/// Gets or sets the name of the shared size group of the column or row.
@@ -22,5 +28,889 @@ namespace Avalonia.Controls
get { return GetValue(SharedSizeGroupProperty); }
set { SetValue(SharedSizeGroupProperty, value); }
}
+ //------------------------------------------------------
+ //
+ // Constructors
+ //
+ //------------------------------------------------------
+
+
+ internal DefinitionBase(bool isColumnDefinition)
+ {
+ _isColumnDefinition = isColumnDefinition;
+ _parentIndex = -1;
+ }
+
+
+
+ //------------------------------------------------------
+ //
+ // Internal Methods
+ //
+ //------------------------------------------------------
+
+ #region Internal Methods
+
+ ///
+ /// Callback to notify about entering model tree.
+ ///
+ internal void OnEnterParentTree()
+ {
+ if (_sharedState == null)
+ {
+ // start with getting SharedSizeGroup value.
+ // this property is NOT inhereted which should result in better overall perf.
+ string sharedSizeGroupId = SharedSizeGroup;
+ if (sharedSizeGroupId != null)
+ {
+ SharedSizeScope privateSharedSizeScope = PrivateSharedSizeScope;
+ if (privateSharedSizeScope != null)
+ {
+ _sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
+ _sharedState.AddMember(this);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Callback to notify about exitting model tree.
+ ///
+ internal void OnExitParentTree()
+ {
+ _offset = 0;
+ if (_sharedState != null)
+ {
+ _sharedState.RemoveMember(this);
+ _sharedState = null;
+ }
+ }
+
+ ///
+ /// Performs action preparing definition to enter layout calculation mode.
+ ///
+ internal void OnBeforeLayout(Grid grid)
+ {
+ // reset layout state.
+ _minSize = 0;
+ LayoutWasUpdated = true;
+
+ // defer verification for shared definitions
+ if (_sharedState != null) { _sharedState.EnsureDeferredValidation(grid); }
+ }
+
+ ///
+ /// Updates min size.
+ ///
+ /// New size.
+ internal void UpdateMinSize(double minSize)
+ {
+ _minSize = Math.Max(_minSize, minSize);
+ }
+
+ ///
+ /// Sets min size.
+ ///
+ /// New size.
+ internal void SetMinSize(double minSize)
+ {
+ _minSize = minSize;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static void OnUserSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ DefinitionBase definition = (DefinitionBase)d;
+
+ if (definition.InParentLogicalTree)
+ {
+ if (definition._sharedState != null)
+ {
+ definition._sharedState.Invalidate();
+ }
+ else
+ {
+ Grid parentGrid = (Grid)definition.Parent;
+
+ if (((GridLength)e.OldValue).GridUnitType != ((GridLength)e.NewValue).GridUnitType)
+ {
+ parentGrid.Invalidate();
+ }
+ else
+ {
+ parentGrid.InvalidateMeasure();
+ }
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static bool IsUserSizePropertyValueValid(object value)
+ {
+ return (((GridLength)value).Value >= 0);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static void OnUserMinSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ DefinitionBase definition = (DefinitionBase)d;
+
+ if (definition.InParentLogicalTree)
+ {
+ Grid parentGrid = (Grid)definition.Parent;
+ parentGrid.InvalidateMeasure();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static bool IsUserMinSizePropertyValueValid(object value)
+ {
+ double v = (double)value;
+ return (!DoubleUtil.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static void OnUserMaxSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ DefinitionBase definition = (DefinitionBase)d;
+
+ if (definition.InParentLogicalTree)
+ {
+ Grid parentGrid = (Grid)definition.Parent;
+ parentGrid.InvalidateMeasure();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static bool IsUserMaxSizePropertyValueValid(object value)
+ {
+ double v = (double)value;
+ return (!DoubleUtil.IsNaN(v) && v >= 0.0d);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// This method reflects Grid.SharedScopeProperty state by setting / clearing
+ /// dynamic property PrivateSharedSizeScopeProperty. Value of PrivateSharedSizeScopeProperty
+ /// is a collection of SharedSizeState objects for the scope.
+ /// Also PrivateSharedSizeScopeProperty is FrameworkPropertyMetadataOptions.Inherits property. So that all children
+ /// elements belonging to a certain scope can easily access SharedSizeState collection. As well
+ /// as been norified about enter / exit a scope.
+ ///
+ internal static void OnIsSharedSizeScopePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ // is it possible to optimize here something like this:
+ // if ((bool)d.GetValue(Grid.IsSharedSizeScopeProperty) == (d.GetLocalValue(PrivateSharedSizeScopeProperty) != null)
+ // { /* do nothing */ }
+ if ((bool)e.NewValue)
+ {
+ SharedSizeScope sharedStatesCollection = new SharedSizeScope();
+ d.SetValue(PrivateSharedSizeScopeProperty, sharedStatesCollection);
+ }
+ else
+ {
+ d.ClearValue(PrivateSharedSizeScopeProperty);
+ }
+ }
+
+ #endregion Internal Methods
+
+ //------------------------------------------------------
+ //
+ // Internal Properties
+ //
+ //------------------------------------------------------
+
+ #region Internal Properties
+
+ ///
+ /// Returns true if this definition is a part of shared group.
+ ///
+ internal bool IsShared
+ {
+ get { return (_sharedState != null); }
+ }
+
+ ///
+ /// Internal accessor to user size field.
+ ///
+ internal GridLength UserSize
+ {
+ get { return (_sharedState != null ? _sharedState.UserSize : UserSizeValueCache); }
+ }
+
+ ///
+ /// Internal accessor to user min size field.
+ ///
+ internal double UserMinSize
+ {
+ get { return (UserMinSizeValueCache); }
+ }
+
+ ///
+ /// Internal accessor to user max size field.
+ ///
+ internal double UserMaxSize
+ {
+ get { return (UserMaxSizeValueCache); }
+ }
+
+ ///
+ /// DefinitionBase's index in the parents collection.
+ ///
+ internal int Index
+ {
+ get
+ {
+ return (_parentIndex);
+ }
+ set
+ {
+ Debug.Assert(value >= -1 && _parentIndex != value);
+ _parentIndex = value;
+ }
+ }
+
+ ///
+ /// Layout-time user size type.
+ ///
+ internal Grid.LayoutTimeSizeType SizeType
+ {
+ get { return (_sizeType); }
+ set { _sizeType = value; }
+ }
+
+ ///
+ /// Returns or sets measure size for the definition.
+ ///
+ internal double MeasureSize
+ {
+ get { return (_measureSize); }
+ set { _measureSize = value; }
+ }
+
+ ///
+ /// Returns definition's layout time type sensitive preferred size.
+ ///
+ ///
+ /// Returned value is guaranteed to be true preferred size.
+ ///
+ internal double PreferredSize
+ {
+ get
+ {
+ double preferredSize = MinSize;
+ if (_sizeType != Grid.LayoutTimeSizeType.Auto
+ && preferredSize < _measureSize)
+ {
+ preferredSize = _measureSize;
+ }
+ return (preferredSize);
+ }
+ }
+
+ ///
+ /// Returns or sets size cache for the definition.
+ ///
+ internal double SizeCache
+ {
+ get { return (_sizeCache); }
+ set { _sizeCache = value; }
+ }
+
+ ///
+ /// Returns min size.
+ ///
+ internal double MinSize
+ {
+ get
+ {
+ double minSize = _minSize;
+ if (UseSharedMinimum
+ && _sharedState != null
+ && minSize < _sharedState.MinSize)
+ {
+ minSize = _sharedState.MinSize;
+ }
+ return (minSize);
+ }
+ }
+
+ ///
+ /// Returns min size, always taking into account shared state.
+ ///
+ internal double MinSizeForArrange
+ {
+ get
+ {
+ double minSize = _minSize;
+ if (_sharedState != null
+ && (UseSharedMinimum || !LayoutWasUpdated)
+ && minSize < _sharedState.MinSize)
+ {
+ minSize = _sharedState.MinSize;
+ }
+ return (minSize);
+ }
+ }
+
+ ///
+ /// Offset.
+ ///
+ internal double FinalOffset
+ {
+ get { return _offset; }
+ set { _offset = value; }
+ }
+
+ ///
+ /// Internal helper to access up-to-date UserSize property value.
+ ///
+ internal GridLength UserSizeValueCache
+ {
+ get
+ {
+ return (GridLength)GetValue(
+ _isColumnDefinition ?
+ ColumnDefinition.WidthProperty :
+ RowDefinition.HeightProperty);
+ }
+ }
+
+ ///
+ /// Internal helper to access up-to-date UserMinSize property value.
+ ///
+ internal double UserMinSizeValueCache
+ {
+ get
+ {
+ return (double)GetValue(
+ _isColumnDefinition ?
+ ColumnDefinition.MinWidthProperty :
+ RowDefinition.MinHeightProperty);
+ }
+ }
+
+ ///
+ /// Internal helper to access up-to-date UserMaxSize property value.
+ ///
+ internal double UserMaxSizeValueCache
+ {
+ get
+ {
+ return (double)GetValue(
+ _isColumnDefinition ?
+ ColumnDefinition.MaxWidthProperty :
+ RowDefinition.MaxHeightProperty);
+ }
+ }
+
+ ///
+ /// Protected. Returns true if this DefinitionBase instance is in parent's logical tree.
+ ///
+ internal bool InParentLogicalTree
+ {
+ get { return (_parentIndex != -1); }
+ }
+
+ #endregion Internal Properties
+
+ //------------------------------------------------------
+ //
+ // Private Methods
+ //
+ //------------------------------------------------------
+
+ #region Private Methods
+
+ ///
+ /// SetFlags is used to set or unset one or multiple
+ /// flags on the object.
+ ///
+ private void SetFlags(bool value, Flags flags)
+ {
+ _flags = value ? (_flags | flags) : (_flags & (~flags));
+ }
+
+ ///
+ /// CheckFlagsAnd returns true if all the flags in the
+ /// given bitmask are set on the object.
+ ///
+ private bool CheckFlagsAnd(Flags flags)
+ {
+ return ((_flags & flags) == flags);
+ }
+
+ ///
+ ///
+ ///
+ private static void OnSharedSizeGroupPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ DefinitionBase definition = (DefinitionBase)e.Sender;
+
+ if (definition.InParentLogicalTree)
+ {
+ string sharedSizeGroupId = (string)e.NewValue;
+
+ if (definition._sharedState != null)
+ {
+ // if definition is already registered AND shared size group id is changing,
+ // then un-register the definition from the current shared size state object.
+ definition._sharedState.RemoveMember(definition);
+ definition._sharedState = null;
+ }
+
+ if ((definition._sharedState == null) && (sharedSizeGroupId != null))
+ {
+ SharedSizeScope privateSharedSizeScope = definition.PrivateSharedSizeScope;
+ if (privateSharedSizeScope != null)
+ {
+ // if definition is not registered and both: shared size group id AND private shared scope
+ // are available, then register definition.
+ definition._sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
+ definition._sharedState.AddMember(definition);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Verifies that Shared Size Group Property string
+ /// a) not empty.
+ /// b) contains only letters, digits and underscore ('_').
+ /// c) does not start with a digit.
+ ///
+ private static bool SharedSizeGroupPropertyValueValid(string value)
+ {
+ // null is default value
+ if (value == null)
+ {
+ return (true);
+ }
+
+ string id = (string)value;
+
+ if (id != string.Empty)
+ {
+ int i = -1;
+ while (++i < id.Length)
+ {
+ bool isDigit = Char.IsDigit(id[i]);
+
+ if ((i == 0 && isDigit)
+ || !(isDigit
+ || Char.IsLetter(id[i])
+ || '_' == id[i]))
+ {
+ break;
+ }
+ }
+
+ if (i == id.Length)
+ {
+ return (true);
+ }
+ }
+
+ return (false);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// OnPrivateSharedSizeScopePropertyChanged is called when new scope enters or
+ /// existing scope just left. In both cases if the DefinitionBase object is already registered
+ /// in SharedSizeState, it should un-register and register itself in a new one.
+ ///
+ private static void OnPrivateSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
+ {
+ DefinitionBase definition = (DefinitionBase)d;
+
+ if (definition.InParentLogicalTree)
+ {
+ SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue;
+
+ if (definition._sharedState != null)
+ {
+ // if definition is already registered And shared size scope is changing,
+ // then un-register the definition from the current shared size state object.
+ definition._sharedState.RemoveMember(definition);
+ definition._sharedState = null;
+ }
+
+ if ((definition._sharedState == null) && (privateSharedSizeScope != null))
+ {
+ string sharedSizeGroup = definition.SharedSizeGroup;
+ if (sharedSizeGroup != null)
+ {
+ // if definition is not registered and both: shared size group id AND private shared scope
+ // are available, then register definition.
+ definition._sharedState = privateSharedSizeScope.EnsureSharedState(definition.SharedSizeGroup);
+ definition._sharedState.AddMember(definition);
+ }
+ }
+ }
+ }
+
+ #endregion Private Methods
+
+ //------------------------------------------------------
+ //
+ // Private Properties
+ //
+ //------------------------------------------------------
+
+ #region Private Properties
+
+ ///
+ /// Private getter of shared state collection dynamic property.
+ ///
+ private SharedSizeScope PrivateSharedSizeScope
+ {
+ get { return (SharedSizeScope)GetValue(PrivateSharedSizeScopeProperty); }
+ }
+
+ ///
+ /// Convenience accessor to UseSharedMinimum flag
+ ///
+ private bool UseSharedMinimum
+ {
+ get { return (CheckFlagsAnd(Flags.UseSharedMinimum)); }
+ set { SetFlags(value, Flags.UseSharedMinimum); }
+ }
+
+ ///
+ /// Convenience accessor to LayoutWasUpdated flag
+ ///
+ private bool LayoutWasUpdated
+ {
+ get { return (CheckFlagsAnd(Flags.LayoutWasUpdated)); }
+ set { SetFlags(value, Flags.LayoutWasUpdated); }
+ }
+
+ #endregion Private Properties
+
+ //------------------------------------------------------
+ //
+ // Private Fields
+ //
+ //------------------------------------------------------
+
+ #region Private Fields
+ private readonly bool _isColumnDefinition; // when "true", this is a ColumnDefinition; when "false" this is a RowDefinition (faster than a type check)
+ private Flags _flags; // flags reflecting various aspects of internal state
+ private int _parentIndex; // this instance's index in parent's children collection
+
+ private Grid.LayoutTimeSizeType _sizeType; // layout-time user size type. it may differ from _userSizeValueCache.UnitType when calculating "to-content"
+
+ private double _minSize; // used during measure to accumulate size for "Auto" and "Star" DefinitionBase's
+ private double _measureSize; // size, calculated to be the input contstraint size for Child.Measure
+ private double _sizeCache; // cache used for various purposes (sorting, caching, etc) during calculations
+ private double _offset; // offset of the DefinitionBase from left / top corner (assuming LTR case)
+
+ private SharedSizeState _sharedState; // reference to shared state object this instance is registered with
+
+ internal const bool ThisIsColumnDefinition = true;
+ internal const bool ThisIsRowDefinition = false;
+
+ #endregion Private Fields
+
+ //------------------------------------------------------
+ //
+ // Private Structures / Classes
+ //
+ //------------------------------------------------------
+
+ #region Private Structures Classes
+
+ [System.Flags]
+ private enum Flags : byte
+ {
+ //
+ // bool flags
+ //
+ UseSharedMinimum = 0x00000020, // when "1", definition will take into account shared state's minimum
+ LayoutWasUpdated = 0x00000040, // set to "1" every time the parent grid is measured
+ }
+
+ ///
+ /// Collection of shared states objects for a single scope
+ ///
+ private class SharedSizeScope
+ {
+ ///
+ /// Returns SharedSizeState object for a given group.
+ /// Creates a new StatedState object if necessary.
+ ///
+ internal SharedSizeState EnsureSharedState(string sharedSizeGroup)
+ {
+ // check that sharedSizeGroup is not default
+ Debug.Assert(sharedSizeGroup != null);
+
+ SharedSizeState sharedState = _registry[sharedSizeGroup] as SharedSizeState;
+ if (sharedState == null)
+ {
+ sharedState = new SharedSizeState(this, sharedSizeGroup);
+ _registry[sharedSizeGroup] = sharedState;
+ }
+ return (sharedState);
+ }
+
+ ///
+ /// Removes an entry in the registry by the given key.
+ ///
+ internal void Remove(object key)
+ {
+ Debug.Assert(_registry.Contains(key));
+ _registry.Remove(key);
+ }
+
+ private Hashtable _registry = new Hashtable(); // storage for shared state objects
+ }
+
+ ///
+ /// Implementation of per shared group state object
+ ///
+ private class SharedSizeState
+ {
+ ///
+ /// Default ctor.
+ ///
+ internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
+ {
+ Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
+ _sharedSizeScope = sharedSizeScope;
+ _sharedSizeGroupId = sharedSizeGroupId;
+ _registry = new List();
+ _layoutUpdated = new EventHandler(OnLayoutUpdated);
+ _broadcastInvalidation = true;
+ }
+
+ ///
+ /// Adds / registers a definition instance.
+ ///
+ internal void AddMember(DefinitionBase member)
+ {
+ Debug.Assert(!_registry.Contains(member));
+ _registry.Add(member);
+ Invalidate();
+ }
+
+ ///
+ /// Removes / un-registers a definition instance.
+ ///
+ ///
+ /// If the collection of registered definitions becomes empty
+ /// instantiates self removal from owner's collection.
+ ///
+ internal void RemoveMember(DefinitionBase member)
+ {
+ Invalidate();
+ _registry.Remove(member);
+
+ if (_registry.Count == 0)
+ {
+ _sharedSizeScope.Remove(_sharedSizeGroupId);
+ }
+ }
+
+ ///
+ /// Propogates invalidations for all registered definitions.
+ /// Resets its own state.
+ ///
+ internal void Invalidate()
+ {
+ _userSizeValid = false;
+
+ if (_broadcastInvalidation)
+ {
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ Grid parentGrid = (Grid)(_registry[i].Parent);
+ parentGrid.Invalidate();
+ }
+ _broadcastInvalidation = false;
+ }
+ }
+
+ ///
+ /// Makes sure that one and only one layout updated handler is registered for this shared state.
+ ///
+ internal void EnsureDeferredValidation(IControl layoutUpdatedHost)
+ {
+ if (_layoutUpdatedHost == null)
+ {
+ _layoutUpdatedHost = layoutUpdatedHost;
+ _layoutUpdatedHost.LayoutUpdated += _layoutUpdated;
+ }
+ }
+
+ ///
+ /// DefinitionBase's specific code.
+ ///
+ internal double MinSize
+ {
+ get
+ {
+ if (!_userSizeValid) { EnsureUserSizeValid(); }
+ return (_minSize);
+ }
+ }
+
+ ///
+ /// DefinitionBase's specific code.
+ ///
+ internal GridLength UserSize
+ {
+ get
+ {
+ if (!_userSizeValid) { EnsureUserSizeValid(); }
+ return (_userSize);
+ }
+ }
+
+ private void EnsureUserSizeValid()
+ {
+ _userSize = new GridLength(1, GridUnitType.Auto);
+
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ Debug.Assert(_userSize.GridUnitType == GridUnitType.Auto
+ || _userSize.GridUnitType == GridUnitType.Pixel);
+
+ GridLength currentGridLength = _registry[i].UserSizeValueCache;
+ if (currentGridLength.GridUnitType == GridUnitType.Pixel)
+ {
+ if (_userSize.GridUnitType == GridUnitType.Auto)
+ {
+ _userSize = currentGridLength;
+ }
+ else if (_userSize.Value < currentGridLength.Value)
+ {
+ _userSize = currentGridLength;
+ }
+ }
+ }
+ // taking maximum with user size effectively prevents squishy-ness.
+ // this is a "solution" to avoid shared definitions from been sized to
+ // different final size at arrange time, if / when different grids receive
+ // different final sizes.
+ _minSize = _userSize.IsAbsolute ? _userSize.Value : 0.0;
+
+ _userSizeValid = true;
+ }
+
+ ///
+ /// OnLayoutUpdated handler. Validates that all participating definitions
+ /// have updated min size value. Forces another layout update cycle if needed.
+ ///
+ private void OnLayoutUpdated(object sender, EventArgs e)
+ {
+ double sharedMinSize = 0;
+
+ // accumulate min size of all participating definitions
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
+ }
+
+ bool sharedMinSizeChanged = !DoubleUtil.AreClose(_minSize, sharedMinSize);
+
+ // compare accumulated min size with min sizes of the individual definitions
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ DefinitionBase definitionBase = _registry[i];
+
+ if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
+ {
+ // if definition's min size is different, then need to re-measure
+ if (!DoubleUtil.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 (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache))
+ {
+ Grid parentGrid = (Grid)definitionBase.Parent;
+ parentGrid.InvalidateArrange();
+ }
+ }
+
+ definitionBase.LayoutWasUpdated = false;
+ }
+ }
+
+ _minSize = sharedMinSize;
+
+ _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
+ _layoutUpdatedHost = null;
+
+ _broadcastInvalidation = true;
+ }
+
+ private readonly SharedSizeScope _sharedSizeScope; // the scope this state belongs to
+ private readonly string _sharedSizeGroupId; // Id of the shared size group this object is servicing
+ private readonly List _registry; // registry of participating definitions
+ private readonly EventHandler _layoutUpdated; // instance event handler for layout updated event
+ private IControl _layoutUpdatedHost; // IControl for which layout updated event handler is registered
+ private bool _broadcastInvalidation; // "true" when broadcasting of invalidation is needed
+ private bool _userSizeValid; // "true" when _userSize is up to date
+ private GridLength _userSize; // shared state
+ private double _minSize; // shared state
+ }
+
+
+ ///
+ /// Static ctor. Used for static registration of properties.
+ ///
+ static DefinitionBase()
+ {
+ SharedSizeGroupProperty.Changed.AddClassHandler(OnSharedSizeGroupPropertyChanged);
+ }
+
+ #endregion Properties
}
-}
\ No newline at end of file
+}
+
+
diff --git a/src/Avalonia.Controls/GridWPF.cs b/src/Avalonia.Controls/GridWPF.cs
index 0c8f571506..96543fda81 100644
--- a/src/Avalonia.Controls/GridWPF.cs
+++ b/src/Avalonia.Controls/GridWPF.cs
@@ -868,7 +868,7 @@ namespace Avalonia.Controls
{
for (int i = 0; i < minSizes.Length; i++)
{
- if (DoubleUtil.GreaterThanOrClose(minSizes[i], 0))
+ if (MathUtilities.GreaterThanOrClose(minSizes[i], 0))
{
if (isRows)
{
@@ -926,7 +926,7 @@ namespace Avalonia.Controls
MeasureCell(i, forceInfinityV);
- hasDesiredSizeUChanged |= !DoubleUtil.AreClose(oldWidth, Children[i].DesiredSize.Width);
+ hasDesiredSizeUChanged |= !MathUtilities.AreClose(oldWidth, Children[i].DesiredSize.Width);
if (!ignoreDesiredSizeU)
{
@@ -1284,10 +1284,10 @@ namespace Avalonia.Controls
// sanity check: totalRemainingSize and sizeToDistribute must be real positive numbers
Debug.Assert(!double.IsInfinity(totalRemainingSize)
- && !DoubleUtil.IsNaN(totalRemainingSize)
+ && !MathUtilities.IsNaN(totalRemainingSize)
&& totalRemainingSize > 0
&& !double.IsInfinity(sizeToDistribute)
- && !DoubleUtil.IsNaN(sizeToDistribute)
+ && !MathUtilities.IsNaN(sizeToDistribute)
&& sizeToDistribute > 0);
for (int i = 0; i < count; ++i)
@@ -1320,107 +1320,6 @@ namespace Avalonia.Controls
///
/// Must initialize LayoutSize for all Star entries in given array of definitions.
///
- private void ResolveStar(
- DefinitionBase[] definitions,
- double availableSize)
- {
- if (FrameworkAppContextSwitches.GridStarDefinitionsCanExceedAvailableSpace)
- {
- ResolveStarLegacy(definitions, availableSize);
- }
- else
- {
- ResolveStarMaxDiscrepancy(definitions, availableSize);
- }
- }
-
- // original implementation, used from 3.0 through 4.6.2
- private void ResolveStarLegacy(
- DefinitionBase[] definitions,
- double availableSize)
- {
- DefinitionBase[] tempDefinitions = TempDefinitions;
- int starDefinitionsCount = 0;
- double takenSize = 0;
-
- for (int i = 0; i < definitions.Length; ++i)
- {
- switch (definitions[i].SizeType)
- {
- case (LayoutTimeSizeType.Auto):
- takenSize += definitions[i].MinSize;
- break;
- case (LayoutTimeSizeType.Pixel):
- takenSize += definitions[i].MeasureSize;
- break;
- case (LayoutTimeSizeType.Star):
- {
- tempDefinitions[starDefinitionsCount++] = definitions[i];
-
- double starValue = definitions[i].UserSize.Value;
-
- if (_IsZero(starValue))
- {
- definitions[i].MeasureSize = 0;
- definitions[i].SizeCache = 0;
- }
- else
- {
- // clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values
- // can be summed up without overflow
- starValue = Math.Min(starValue, c_starClip);
-
- // Note: normalized star value is temporary cached into MeasureSize
- definitions[i].MeasureSize = starValue;
- double maxSize = Math.Max(definitions[i].MinSize, definitions[i].UserMaxSize);
- maxSize = Math.Min(maxSize, c_starClip);
- definitions[i].SizeCache = maxSize / starValue;
- }
- }
- break;
- }
- }
-
- if (starDefinitionsCount > 0)
- {
- Array.Sort(tempDefinitions, 0, starDefinitionsCount, s_starDistributionOrderComparer);
-
- // the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow...
- // partial sum value is stored in each definition's SizeCache member.
- // this way the algorithm guarantees (starValue <= definition.SizeCache) and thus
- // (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero.
- // this is an important change from previous implementation where the following was possible:
- // ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0...
- double allStarWeights = 0;
- int i = starDefinitionsCount - 1;
- do
- {
- allStarWeights += tempDefinitions[i].MeasureSize;
- tempDefinitions[i].SizeCache = allStarWeights;
- } while (--i >= 0);
-
- i = 0;
- do
- {
- double resolvedSize;
- double starValue = tempDefinitions[i].MeasureSize;
-
- if (_IsZero(starValue))
- {
- resolvedSize = tempDefinitions[i].MinSize;
- }
- else
- {
- double userSize = Math.Max(availableSize - takenSize, 0.0) * (starValue / tempDefinitions[i].SizeCache);
- resolvedSize = Math.Min(userSize, tempDefinitions[i].UserMaxSize);
- resolvedSize = Math.Max(tempDefinitions[i].MinSize, resolvedSize);
- }
-
- tempDefinitions[i].MeasureSize = resolvedSize;
- takenSize += resolvedSize;
- } while (++i < starDefinitionsCount);
- }
- }
// new implementation as of 4.7. Several improvements:
// 1. Allocate to *-defs hitting their min or max constraints, before allocating
@@ -1435,7 +1334,8 @@ namespace Avalonia.Controls
// discrepancy (defined below). This avoids discontinuities - small
// change in available space resulting in large change to one def's allocation.
// 3. Correct handling of large *-values, including Infinity.
- private void ResolveStarMaxDiscrepancy(
+
+ private void ResolveStar(
DefinitionBase[] definitions,
double availableSize)
{
@@ -1755,272 +1655,7 @@ namespace Avalonia.Controls
DefinitionBase[] definitions,
double finalSize,
bool columns)
- {
- if (FrameworkAppContextSwitches.GridStarDefinitionsCanExceedAvailableSpace)
- {
- SetFinalSizeLegacy(definitions, finalSize, columns);
- }
- else
- {
- SetFinalSizeMaxDiscrepancy(definitions, finalSize, columns);
- }
- }
-
- // original implementation, used from 3.0 through 4.6.2
- private void SetFinalSizeLegacy(
- DefinitionBase[] definitions,
- double finalSize,
- bool columns)
- {
- int starDefinitionsCount = 0; // traverses form the first entry up
- int nonStarIndex = definitions.Length; // traverses from the last entry down
- double allPreferredArrangeSize = 0;
- bool useLayoutRounding = this.UseLayoutRounding;
- int[] definitionIndices = DefinitionIndices;
- double[] roundingErrors = null;
-
- // If using layout rounding, check whether rounding needs to compensate for high DPI
- double dpi = 1.0;
-
- if (useLayoutRounding)
- {
- DpiScale dpiScale = GetDpi();
- dpi = columns ? dpiScale.DpiScaleX : dpiScale.DpiScaleY;
- roundingErrors = RoundingErrors;
- }
-
- for (int i = 0; i < definitions.Length; ++i)
- {
- // if definition is shared then is cannot be star
- Debug.Assert(!definitions[i].IsShared || !definitions[i].UserSize.IsStar);
-
- if (definitions[i].UserSize.IsStar)
- {
- double starValue = definitions[i].UserSize.Value;
-
- if (_IsZero(starValue))
- {
- // cach normilized star value temporary into MeasureSize
- definitions[i].MeasureSize = 0;
- definitions[i].SizeCache = 0;
- }
- else
- {
- // clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values
- // can be summed up without overflow
- starValue = Math.Min(starValue, c_starClip);
-
- // Note: normalized star value is temporary cached into MeasureSize
- definitions[i].MeasureSize = starValue;
- double maxSize = Math.Max(definitions[i].MinSizeForArrange, definitions[i].UserMaxSize);
- maxSize = Math.Min(maxSize, c_starClip);
- definitions[i].SizeCache = maxSize / starValue;
- if (useLayoutRounding)
- {
- roundingErrors[i] = definitions[i].SizeCache;
- definitions[i].SizeCache = IControl.RoundLayoutValue(definitions[i].SizeCache, dpi);
- }
- }
- definitionIndices[starDefinitionsCount++] = i;
- }
- else
- {
- double userSize = 0;
-
- switch (definitions[i].UserSize.GridUnitType)
- {
- case (GridUnitType.Pixel):
- userSize = definitions[i].UserSize.Value;
- break;
-
- case (GridUnitType.Auto):
- userSize = definitions[i].MinSizeForArrange;
- break;
- }
-
- double userMaxSize;
-
- if (definitions[i].IsShared)
- {
- // overriding userMaxSize effectively prevents squishy-ness.
- // this is a "solution" to avoid shared definitions from been sized to
- // different final size at arrange time, if / when different grids receive
- // different final sizes.
- userMaxSize = userSize;
- }
- else
- {
- userMaxSize = definitions[i].UserMaxSize;
- }
-
- definitions[i].SizeCache = Math.Max(definitions[i].MinSizeForArrange, Math.Min(userSize, userMaxSize));
- if (useLayoutRounding)
- {
- roundingErrors[i] = definitions[i].SizeCache;
- definitions[i].SizeCache = IControl.RoundLayoutValue(definitions[i].SizeCache, dpi);
- }
-
- allPreferredArrangeSize += definitions[i].SizeCache;
- definitionIndices[--nonStarIndex] = i;
- }
- }
-
- // indices should meet
- Debug.Assert(nonStarIndex == starDefinitionsCount);
-
- if (starDefinitionsCount > 0)
- {
- StarDistributionOrderIndexComparer starDistributionOrderIndexComparer = new StarDistributionOrderIndexComparer(definitions);
- Array.Sort(definitionIndices, 0, starDefinitionsCount, starDistributionOrderIndexComparer);
-
- // the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow...
- // partial sum value is stored in each definition's SizeCache member.
- // this way the algorithm guarantees (starValue <= definition.SizeCache) and thus
- // (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero.
- // this is an important change from previous implementation where the following was possible:
- // ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0...
- double allStarWeights = 0;
- int i = starDefinitionsCount - 1;
- do
- {
- allStarWeights += definitions[definitionIndices[i]].MeasureSize;
- definitions[definitionIndices[i]].SizeCache = allStarWeights;
- } while (--i >= 0);
-
- i = 0;
- do
- {
- double resolvedSize;
- double starValue = definitions[definitionIndices[i]].MeasureSize;
-
- if (_IsZero(starValue))
- {
- resolvedSize = definitions[definitionIndices[i]].MinSizeForArrange;
- }
- else
- {
- double userSize = Math.Max(finalSize - allPreferredArrangeSize, 0.0) * (starValue / definitions[definitionIndices[i]].SizeCache);
- resolvedSize = Math.Min(userSize, definitions[definitionIndices[i]].UserMaxSize);
- resolvedSize = Math.Max(definitions[definitionIndices[i]].MinSizeForArrange, resolvedSize);
- }
-
- definitions[definitionIndices[i]].SizeCache = resolvedSize;
- if (useLayoutRounding)
- {
- roundingErrors[definitionIndices[i]] = definitions[definitionIndices[i]].SizeCache;
- definitions[definitionIndices[i]].SizeCache = IControl.RoundLayoutValue(definitions[definitionIndices[i]].SizeCache, dpi);
- }
-
- allPreferredArrangeSize += definitions[definitionIndices[i]].SizeCache;
- } while (++i < starDefinitionsCount);
- }
-
- if (allPreferredArrangeSize > finalSize
- && !_AreClose(allPreferredArrangeSize, finalSize))
- {
- DistributionOrderIndexComparer distributionOrderIndexComparer = new DistributionOrderIndexComparer(definitions);
- Array.Sort(definitionIndices, 0, definitions.Length, distributionOrderIndexComparer);
- double sizeToDistribute = finalSize - allPreferredArrangeSize;
-
- for (int i = 0; i < definitions.Length; ++i)
- {
- int definitionIndex = definitionIndices[i];
- double final = definitions[definitionIndex].SizeCache + (sizeToDistribute / (definitions.Length - i));
- double finalOld = final;
- final = Math.Max(final, definitions[definitionIndex].MinSizeForArrange);
- final = Math.Min(final, definitions[definitionIndex].SizeCache);
-
- if (useLayoutRounding)
- {
- roundingErrors[definitionIndex] = final;
- final = IControl.RoundLayoutValue(finalOld, dpi);
- final = Math.Max(final, definitions[definitionIndex].MinSizeForArrange);
- final = Math.Min(final, definitions[definitionIndex].SizeCache);
- }
-
- sizeToDistribute -= (final - definitions[definitionIndex].SizeCache);
- definitions[definitionIndex].SizeCache = final;
- }
-
- allPreferredArrangeSize = finalSize - sizeToDistribute;
- }
-
- if (useLayoutRounding)
- {
- if (!_AreClose(allPreferredArrangeSize, finalSize))
- {
- // Compute deltas
- for (int i = 0; i < definitions.Length; ++i)
- {
- roundingErrors[i] = roundingErrors[i] - definitions[i].SizeCache;
- definitionIndices[i] = i;
- }
-
- // Sort rounding errors
- RoundingErrorIndexComparer roundingErrorIndexComparer = new RoundingErrorIndexComparer(roundingErrors);
- Array.Sort(definitionIndices, 0, definitions.Length, roundingErrorIndexComparer);
- double adjustedSize = allPreferredArrangeSize;
- double dpiIncrement = IControl.RoundLayoutValue(1.0, dpi);
-
- if (allPreferredArrangeSize > finalSize)
- {
- int i = definitions.Length - 1;
- while ((adjustedSize > finalSize && !_AreClose(adjustedSize, finalSize)) && i >= 0)
- {
- DefinitionBase definition = definitions[definitionIndices[i]];
- double final = definition.SizeCache - dpiIncrement;
- final = Math.Max(final, definition.MinSizeForArrange);
- if (final < definition.SizeCache)
- {
- adjustedSize -= dpiIncrement;
- }
- definition.SizeCache = final;
- i--;
- }
- }
- else if (allPreferredArrangeSize < finalSize)
- {
- int i = 0;
- while ((adjustedSize < finalSize && !_AreClose(adjustedSize, finalSize)) && i < definitions.Length)
- {
- DefinitionBase definition = definitions[definitionIndices[i]];
- double final = definition.SizeCache + dpiIncrement;
- final = Math.Max(final, definition.MinSizeForArrange);
- if (final > definition.SizeCache)
- {
- adjustedSize += dpiIncrement;
- }
- definition.SizeCache = final;
- i++;
- }
- }
- }
- }
-
- definitions[0].FinalOffset = 0.0;
- for (int i = 0; i < definitions.Length; ++i)
- {
- definitions[(i + 1) % definitions.Length].FinalOffset = definitions[i].FinalOffset + definitions[i].SizeCache;
- }
- }
-
- // new implementation, as of 4.7. This incorporates the same algorithm
- // as in ResolveStarMaxDiscrepancy. It differs in the same way that SetFinalSizeLegacy
- // differs from ResolveStarLegacy, namely (a) leaves results in def.SizeCache
- // instead of def.MeasureSize, (b) implements LayoutRounding if requested,
- // (c) stores intermediate results differently.
- // The LayoutRounding logic is improved:
- // 1. Use pre-rounded values during proportional allocation. This avoids the
- // same kind of problems arising from interaction with min/max that
- // motivated the new algorithm in the first place.
- // 2. Use correct "nudge" amount when distributing roundoff space. This
- // comes into play at high DPI - greater than 134.
- // 3. Applies rounding only to real pixel values (not to ratios)
- private void SetFinalSizeMaxDiscrepancy(
- DefinitionBase[] definitions,
- double finalSize,
- bool columns)
- {
+ {
int defCount = definitions.Length;
int[] definitionIndices = DefinitionIndices;
int minCount = 0, maxCount = 0;
@@ -2359,7 +1994,7 @@ namespace Avalonia.Controls
for (int i = 0; i < definitions.Length; ++i)
{
DefinitionBase def = definitions[i];
- double roundedSize = IControl.RoundLayoutValue(def.SizeCache, dpi);
+ double roundedSize = Control.RoundLayoutValue(def.SizeCache, dpi);
roundingErrors[i] = (roundedSize - def.SizeCache);
def.SizeCache = roundedSize;
roundedTakenSize += roundedSize;
@@ -2561,9 +2196,6 @@ namespace Avalonia.Controls
ExtendedData extData = ExtData;
if (extData != null)
{
- // for (int i = 0; i < PrivateColumnCount; ++i) DefinitionsU[i].SetValid ();
- // for (int i = 0; i < PrivateRowCount; ++i) DefinitionsV[i].SetValid ();
-
if (extData.TempDefinitions != null)
{
// TempDefinitions has to be cleared to avoid "memory leaks"
@@ -3403,7 +3035,7 @@ namespace Avalonia.Controls
internal StarDistributionOrderIndexComparer(DefinitionBase[] definitions)
{
- Invariant.Assert(definitions != null);
+ Contract.Requires(definitions != null);
this.definitions = definitions;
}
@@ -3444,7 +3076,7 @@ namespace Avalonia.Controls
internal DistributionOrderIndexComparer(DefinitionBase[] definitions)
{
- Invariant.Assert(definitions != null);
+ Contract.Requires(definitions != null);
this.definitions = definitions;
}
@@ -3487,7 +3119,7 @@ namespace Avalonia.Controls
internal RoundingErrorIndexComparer(double[] errors)
{
- Invariant.Assert(errors != null);
+ Contract.Requires(errors != null);
this.errors = errors;
}
@@ -3586,7 +3218,7 @@ namespace Avalonia.Controls
internal MinRatioIndexComparer(DefinitionBase[] definitions)
{
- Invariant.Assert(definitions != null);
+ Contract.Requires(definitions != null);
this.definitions = definitions;
}
@@ -3627,7 +3259,7 @@ namespace Avalonia.Controls
internal MaxRatioIndexComparer(DefinitionBase[] definitions)
{
- Invariant.Assert(definitions != null);
+ Contract.Requires(definitions != null);
this.definitions = definitions;
}
@@ -3668,7 +3300,7 @@ namespace Avalonia.Controls
internal StarWeightIndexComparer(DefinitionBase[] definitions)
{
- Invariant.Assert(definitions != null);
+ Contract.Requires(definitions != null);
this.definitions = definitions;
}
@@ -3699,79 +3331,11 @@ namespace Avalonia.Controls
return result;
}
}
-
- ///
- /// Implementation of a simple enumerator of grid's logical Children
- ///
- private class GridChildrenCollectionEnumeratorSimple : IEnumerator
- {
- internal GridChildrenCollectionEnumeratorSimple(Grid grid, bool includeChildren)
- {
- Debug.Assert(grid != null);
- _currentEnumerator = -1;
- _enumerator0 = new ColumnDefinitions.Enumerator(grid.ExtData != null ? grid.ExtData.ColumnDefinitions : null);
- _enumerator1 = new RowDefinitions.Enumerator(grid.ExtData != null ? grid.ExtData.RowDefinitions : null);
- // GridLineRenderer is NOT included into this enumerator.
- _enumerator2Index = 0;
- if (includeChildren)
- {
- _enumerator2Collection = grid.Children;
- _enumerator2Count = _enumerator2Collection.Count;
- }
- else
- {
- _enumerator2Collection = null;
- _enumerator2Count = 0;
- }
- }
-
- public bool MoveNext()
- {
- while (_currentEnumerator < 3)
- {
- if (_currentEnumerator >= 0)
- {
- switch (_currentEnumerator)
- {
- case (0): if (_enumerator0.MoveNext()) { _currentChild = _enumerator0.Current; return (true); } break;
- case (1): if (_enumerator1.MoveNext()) { _currentChild = _enumerator1.Current; return (true); } break;
- case (2):
- if (_enumerator2Index < _enumerator2Count)
- {
- _currentChild = _enumerator2Collection[_enumerator2Index];
- _enumerator2Index++;
- return (true);
- }
- break;
- }
- }
- _currentEnumerator++;
- }
- return (false);
- }
-
- public void Reset()
- {
- _currentEnumerator = -1;
- _currentChild = null;
- _enumerator0.Reset();
- _enumerator1.Reset();
- _enumerator2Index = 0;
- }
-
- private int _currentEnumerator;
- private Object _currentChild;
- private ColumnDefinitions.Enumerator _enumerator0;
- private RowDefinitions.Enumerator _enumerator1;
- private Controls _enumerator2Collection;
- private int _enumerator2Index;
- private int _enumerator2Count;
- }
-
+
///
/// Helper to render grid lines.
///
- internal class GridLinesRenderer : DrawingVisual
+ internal class GridLinesRenderer : Visual
{
///
/// Static initialization