diff --git a/src/Avalonia.Controls/ColumnDefinition.cs b/src/Avalonia.Controls/Grid/ColumnDefinition.cs similarity index 100% rename from src/Avalonia.Controls/ColumnDefinition.cs rename to src/Avalonia.Controls/Grid/ColumnDefinition.cs diff --git a/src/Avalonia.Controls/ColumnDefinitions.cs b/src/Avalonia.Controls/Grid/ColumnDefinitions.cs similarity index 100% rename from src/Avalonia.Controls/ColumnDefinitions.cs rename to src/Avalonia.Controls/Grid/ColumnDefinitions.cs diff --git a/src/Avalonia.Controls/Grid/Grid.cs b/src/Avalonia.Controls/Grid/Grid.cs index 9291426c7f..5def043136 100644 --- a/src/Avalonia.Controls/Grid/Grid.cs +++ b/src/Avalonia.Controls/Grid/Grid.cs @@ -21,31 +21,58 @@ namespace Avalonia.Controls { public class Grid : Panel { - internal bool CellsStructureDirty = true; internal bool SizeToContentU; internal bool SizeToContentV; internal bool HasStarCellsU; internal bool HasStarCellsV; internal bool HasGroup3CellsInAutoRows; - internal bool ColumnDefinitionsDirty = true; - internal bool RowDefinitionsDirty = true; - - // index of the first cell in first cell group + internal bool DefinitionsDirty; + internal bool IsTrivialGrid => (_definitionsU?.Length <= 1) && + (_definitionsV?.Length <= 1); internal int CellGroup1; - - // index of the first cell in second cell group internal int CellGroup2; - - // index of the first cell in third cell group internal int CellGroup3; - - // index of the first cell in fourth cell group internal int CellGroup4; + /// + /// Helper for Comparer methods. + /// + /// + /// true if one or both of x and y are null, in which case result holds + /// the relative sort order. + /// + internal static bool CompareNullRefs(object x, object y, out int result) + { + result = 2; + + if (x == null) + { + if (y == null) + { + result = 0; + } + else + { + result = -1; + } + } + else + { + if (y == null) + { + result = 1; + } + } + + return (result != 2); + } + // temporary array used during layout for various purposes // TempDefinitions.Length == Max(DefinitionsU.Length, DefinitionsV.Length) private DefinitionBase[] _tempDefinitions; + + private GridLinesRenderer _gridLinesRenderer; // Keeps track of definition indices. @@ -55,8 +82,10 @@ namespace Avalonia.Controls // Stores unrounded values and rounding errors during layout rounding. private double[] _roundingErrors; - private DefinitionBase[] _definitionsU; - private DefinitionBase[] _definitionsV; + private ColumnDefinitions _columnDefinitions; + private RowDefinitions _rowDefinitions; + private DefinitionBase[] _definitionsU = new DefinitionBase[1] { new ColumnDefinition() }; + private DefinitionBase[] _definitionsV = new DefinitionBase[1] { new RowDefinition() }; // 5 is an arbitrary constant chosen to end the measure loop private const int _layoutLoopMaxCount = 5; @@ -67,6 +96,78 @@ namespace Avalonia.Controls private static readonly IComparer _maxRatioComparer; private static readonly IComparer _starWeightComparer; + /// + /// Helper accessor to layout time array of definitions. + /// + private DefinitionBase[] TempDefinitions + { + get + { + int requiredLength = Math.Max(_definitionsU.Length, _definitionsV.Length) * 2; + + if (_tempDefinitions == null + || _tempDefinitions.Length < requiredLength) + { + WeakReference tempDefinitionsWeakRef = (WeakReference)Thread.GetData(_tempDefinitionsDataSlot); + if (tempDefinitionsWeakRef == null) + { + _tempDefinitions = new DefinitionBase[requiredLength]; + Thread.SetData(_tempDefinitionsDataSlot, new WeakReference(_tempDefinitions)); + } + else + { + _tempDefinitions = (DefinitionBase[])tempDefinitionsWeakRef.Target; + if (_tempDefinitions == null + || _tempDefinitions.Length < requiredLength) + { + _tempDefinitions = new DefinitionBase[requiredLength]; + tempDefinitionsWeakRef.Target = _tempDefinitions; + } + } + } + return (_tempDefinitions); + } + } + + /// + /// Helper accessor to definition indices. + /// + private int[] DefinitionIndices + { + get + { + int requiredLength = Math.Max(Math.Max(_definitionsU.Length, _definitionsV.Length), 1) * 2; + + if (_definitionIndices == null || _definitionIndices.Length < requiredLength) + { + _definitionIndices = new int[requiredLength]; + } + + return _definitionIndices; + } + } + + /// + /// Helper accessor to rounding errors. + /// + private double[] RoundingErrors + { + get + { + int requiredLength = Math.Max(_definitionsU.Length, _definitionsV.Length); + + if (_roundingErrors == null && requiredLength == 0) + { + _roundingErrors = new double[1]; + } + else if (_roundingErrors == null || _roundingErrors.Length < requiredLength) + { + _roundingErrors = new double[requiredLength]; + } + return _roundingErrors; + } + } + static Grid() { ShowGridLinesProperty.Changed.AddClassHandler(OnShowGridLinesPropertyChanged); @@ -128,6 +229,86 @@ namespace Avalonia.Controls set { SetValue(ShowGridLinesProperty, value); } } + /// + /// Gets or sets the columns definitions for the grid. + /// + public ColumnDefinitions ColumnDefinitions + { + get + { + if (_columnDefinitions == null) + { + ColumnDefinitions = new ColumnDefinitions(); + } + + return _columnDefinitions; + } + set + { + _columnDefinitions = value; + _columnDefinitions.TrackItemPropertyChanged(_ => Invalidate()); + DefinitionsDirty = true; + + if (_columnDefinitions.Count > 0) + _definitionsU = _columnDefinitions.Cast().ToArray(); + + _columnDefinitions.CollectionChanged += delegate + { + if (_columnDefinitions.Count == 0) + { + _definitionsU = new DefinitionBase[1] { new ColumnDefinition() }; + } + else + { + _definitionsU = _columnDefinitions.Cast().ToArray(); + DefinitionsDirty = true; + } + Invalidate(); + }; + } + } + + /// + /// Gets or sets the row definitions for the grid. + /// + public RowDefinitions RowDefinitions + { + get + { + if (_rowDefinitions == null) + { + RowDefinitions = new RowDefinitions(); + } + + return _rowDefinitions; + } + set + { + _rowDefinitions = value; + _rowDefinitions.TrackItemPropertyChanged(_ => Invalidate()); + + DefinitionsDirty = true; + + if (_rowDefinitions.Count > 0) + _definitionsV = _rowDefinitions.Cast().ToArray(); + + _rowDefinitions.CollectionChanged += delegate + { + if (_rowDefinitions.Count == 0) + { + _definitionsV = new DefinitionBase[1] { new RowDefinition() }; + } + else + { + _definitionsV = _rowDefinitions.Cast().ToArray(); + DefinitionsDirty = true; + } + Invalidate(); + }; + } + } + + /// /// Gets the value of the Column attached property for a control. /// @@ -218,95 +399,6 @@ namespace Avalonia.Controls element.SetValue(RowSpanProperty, value); } - private ColumnDefinitions _columnDefinitions; - private RowDefinitions _rowDefinitions; - - /// - /// Gets or sets the columns definitions for the grid. - /// - public ColumnDefinitions ColumnDefinitions - { - get - { - if (_columnDefinitions == null) - { - ColumnDefinitions = new ColumnDefinitions(); - } - - return _columnDefinitions; - } - set - { - _columnDefinitions = value; - _columnDefinitions.TrackItemPropertyChanged(_ => Invalidate()); - ColumnDefinitionsDirty = true; - - if (_columnDefinitions.Count > 0) - _definitionsU = _columnDefinitions.Cast().ToArray(); - else - _definitionsU = new DefinitionBase[1] { new ColumnDefinition() }; - - _columnDefinitions.CollectionChanged += (_, e) => - { - if (_columnDefinitions.Count == 0) - { - _definitionsU = new DefinitionBase[1] { new ColumnDefinition() }; - } - else - { - _definitionsU = _columnDefinitions.Cast().ToArray(); - ColumnDefinitionsDirty = true; - } - Invalidate(); - }; - } - } - - /// - /// Gets or sets the row definitions for the grid. - /// - public RowDefinitions RowDefinitions - { - get - { - if (_rowDefinitions == null) - { - RowDefinitions = new RowDefinitions(); - } - - return _rowDefinitions; - } - set - { - _rowDefinitions = value; - _rowDefinitions.TrackItemPropertyChanged(_ => Invalidate()); - - RowDefinitionsDirty = true; - - if (_rowDefinitions.Count > 0) - _definitionsV = _rowDefinitions.Cast().ToArray(); - else - _definitionsV = new DefinitionBase[1] { new RowDefinition() }; - - _rowDefinitions.CollectionChanged += (_, e) => - { - if (_rowDefinitions.Count == 0) - { - _definitionsV = new DefinitionBase[1] { new RowDefinition() }; - } - else - { - _definitionsV = _rowDefinitions.Cast().ToArray(); - RowDefinitionsDirty = true; - } - Invalidate(); - }; - } - } - - internal bool IsTrivialGrid => (_definitionsU?.Length <= 1) && - (_definitionsV?.Length <= 1); - /// /// Content measurement. /// @@ -341,7 +433,7 @@ namespace Avalonia.Controls bool sizeToContentV = double.IsPositiveInfinity(constraint.Height); // Clear index information and rounding errors - if (RowDefinitionsDirty || ColumnDefinitionsDirty) + if (DefinitionsDirty) { if (_definitionIndices != null) { @@ -357,12 +449,11 @@ namespace Avalonia.Controls _roundingErrors = null; } } + + DefinitionsDirty = false; } - ValidateColumnDefinitionsStructure(); ValidateDefinitionsLayout(_definitionsU, sizeToContentU); - - ValidateRowDefinitionsStructure(); ValidateDefinitionsLayout(_definitionsV, sizeToContentV); CellsStructureDirty |= (SizeToContentU != sizeToContentU) @@ -453,27 +544,6 @@ namespace Avalonia.Controls return (gridDesiredSize); } - private void ValidateColumnDefinitionsStructure() - { - if (ColumnDefinitionsDirty) - { - if (_definitionsU == null) - _definitionsU = new DefinitionBase[1] { new ColumnDefinition() }; - ColumnDefinitionsDirty = false; - } - } - - private void ValidateRowDefinitionsStructure() - { - if (RowDefinitionsDirty) - { - if (_definitionsV == null) - _definitionsV = new DefinitionBase[1] { new RowDefinition() }; - - RowDefinitionsDirty = false; - } - } - /// /// Content arrangement. /// @@ -554,12 +624,12 @@ namespace Avalonia.Controls /// /// Used from public ColumnDefinition ActualWidth. Calculates final width using offset data. /// - internal double GetFinalColumnDefinitionWidth(int columnIndex) + private double GetFinalColumnDefinitionWidth(int columnIndex) { double value = 0.0; // actual value calculations require structure to be up-to-date - if (!ColumnDefinitionsDirty) + if (!DefinitionsDirty) { value = _definitionsU[(columnIndex + 1) % _definitionsU.Length].FinalOffset; if (columnIndex != 0) { value -= _definitionsU[columnIndex].FinalOffset; } @@ -573,12 +643,12 @@ namespace Avalonia.Controls /// /// Used from public RowDefinition ActualHeight. Calculates final height using offset data. /// - internal double GetFinalRowDefinitionHeight(int rowIndex) + private double GetFinalRowDefinitionHeight(int rowIndex) { double value = 0.0; // actual value calculations require structure to be up-to-date - if (!RowDefinitionsDirty) + if (!DefinitionsDirty) { value = _definitionsV[(rowIndex + 1) % _definitionsV.Length].FinalOffset; if (rowIndex != 0) { value -= _definitionsV[rowIndex].FinalOffset; } @@ -589,7 +659,7 @@ namespace Avalonia.Controls /// /// Invalidates grid caches and makes the grid dirty for measure. /// - internal void Invalidate() + private void Invalidate() { CellsStructureDirty = true; InvalidateMeasure(); @@ -2111,14 +2181,11 @@ namespace Avalonia.Controls /// private void SetValid() { - if (IsTrivialGrid) + if (IsTrivialGrid && _tempDefinitions != null) { - if (_tempDefinitions != null) - { - // TempDefinitions has to be cleared to avoid "memory leaks" - Array.Clear(_tempDefinitions, 0, Math.Max(_definitionsU.Length, _definitionsV.Length)); - _tempDefinitions = null; - } + // TempDefinitions has to be cleared to avoid "memory leaks" + Array.Clear(_tempDefinitions, 0, Math.Max(_definitionsU.Length, _definitionsV.Length)); + _tempDefinitions = null; } } @@ -2171,7 +2238,6 @@ namespace Avalonia.Controls return newValue; } - private static int ValidateColumn(AvaloniaObject o, int value) { if (value < 0) @@ -2200,115 +2266,10 @@ namespace Avalonia.Controls } } - /// - /// Helper for Comparer methods. - /// - /// - /// true if one or both of x and y are null, in which case result holds - /// the relative sort order. - /// - internal static bool CompareNullRefs(object x, object y, out int result) - { - result = 2; - - if (x == null) - { - if (y == null) - { - result = 0; - } - else - { - result = -1; - } - } - else - { - if (y == null) - { - result = 1; - } - } - - return (result != 2); - } - - /// - /// Helper accessor to layout time array of definitions. - /// - private DefinitionBase[] TempDefinitions - { - get - { - int requiredLength = Math.Max(_definitionsU.Length, _definitionsV.Length) * 2; - - if (_tempDefinitions == null - || _tempDefinitions.Length < requiredLength) - { - WeakReference tempDefinitionsWeakRef = (WeakReference)Thread.GetData(_tempDefinitionsDataSlot); - if (tempDefinitionsWeakRef == null) - { - _tempDefinitions = new DefinitionBase[requiredLength]; - Thread.SetData(_tempDefinitionsDataSlot, new WeakReference(_tempDefinitions)); - } - else - { - _tempDefinitions = (DefinitionBase[])tempDefinitionsWeakRef.Target; - if (_tempDefinitions == null - || _tempDefinitions.Length < requiredLength) - { - _tempDefinitions = new DefinitionBase[requiredLength]; - tempDefinitionsWeakRef.Target = _tempDefinitions; - } - } - } - return (_tempDefinitions); - } - } - - /// - /// Helper accessor to definition indices. - /// - private int[] DefinitionIndices - { - get - { - int requiredLength = Math.Max(Math.Max(_definitionsU.Length, _definitionsV.Length), 1) * 2; - - if (_definitionIndices == null || _definitionIndices.Length < requiredLength) - { - _definitionIndices = new int[requiredLength]; - } - - return _definitionIndices; - } - } - - /// - /// Helper accessor to rounding errors. - /// - private double[] RoundingErrors - { - get - { - int requiredLength = Math.Max(_definitionsU.Length, _definitionsV.Length); - - if (_roundingErrors == null && requiredLength == 0) - { - _roundingErrors = new double[1]; - } - else if (_roundingErrors == null || _roundingErrors.Length < requiredLength) - { - _roundingErrors = new double[requiredLength]; - } - return _roundingErrors; - } - } - /// /// Returns *-weight, adjusted for scale computed during Phase 1 /// - static double StarWeight(DefinitionBase def, double scale) + private static double StarWeight(DefinitionBase def, double scale) { if (scale < 0.0) { diff --git a/src/Avalonia.Controls/RowDefinition.cs b/src/Avalonia.Controls/Grid/RowDefinition.cs similarity index 100% rename from src/Avalonia.Controls/RowDefinition.cs rename to src/Avalonia.Controls/Grid/RowDefinition.cs diff --git a/src/Avalonia.Controls/RowDefinitions.cs b/src/Avalonia.Controls/Grid/RowDefinitions.cs similarity index 100% rename from src/Avalonia.Controls/RowDefinitions.cs rename to src/Avalonia.Controls/Grid/RowDefinitions.cs