From f9ebfc232d685a9fe8e06c305c1cd52a0058c287 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Thu, 23 May 2019 14:00:08 +0800 Subject: [PATCH] Uncomment Avalonia's Grid and exclude it from build temporarily. --- .../Avalonia.Controls.csproj | 1 + src/Avalonia.Controls/Grid.cs | 1199 ++++++++--------- 2 files changed, 600 insertions(+), 600 deletions(-) diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 32331d29ab..eabe136791 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -14,4 +14,5 @@ + diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 98cd5aad02..ad5e96376d 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -1,601 +1,600 @@ -// // 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; -// using System.Linq; -// using System.Reactive.Linq; -// using System.Runtime.CompilerServices; -// using Avalonia.Collections; -// using Avalonia.Controls.Utils; -// using Avalonia.VisualTree; -// using JetBrains.Annotations; - -// namespace Avalonia.Controls -// { -// /// -// /// Lays out child controls according to a grid. -// /// -// public class Grid : Panel -// { -// /// -// /// Defines the Column attached property. -// /// -// public static readonly AttachedProperty ColumnProperty = -// AvaloniaProperty.RegisterAttached( -// "Column", -// validate: ValidateColumn); - -// /// -// /// Defines the ColumnSpan attached property. -// /// -// public static readonly AttachedProperty ColumnSpanProperty = -// AvaloniaProperty.RegisterAttached("ColumnSpan", 1); - -// /// -// /// Defines the Row attached property. -// /// -// public static readonly AttachedProperty RowProperty = -// AvaloniaProperty.RegisterAttached( -// "Row", -// validate: ValidateRow); - -// /// -// /// Defines the RowSpan attached property. -// /// -// public static readonly AttachedProperty RowSpanProperty = -// AvaloniaProperty.RegisterAttached("RowSpan", 1); - -// public static readonly AttachedProperty IsSharedSizeScopeProperty = -// AvaloniaProperty.RegisterAttached("IsSharedSizeScope", false); - -// protected override void OnMeasureInvalidated() -// { -// base.OnMeasureInvalidated(); -// _sharedSizeHost?.InvalidateMeasure(this); -// } - -// private SharedSizeScopeHost _sharedSizeHost; - -// /// -// /// Defines the SharedSizeScopeHost private property. -// /// The ampersands are used to make accessing the property via xaml inconvenient. -// /// -// internal static readonly AttachedProperty s_sharedSizeScopeHostProperty = -// AvaloniaProperty.RegisterAttached("&&SharedSizeScopeHost"); - -// private ColumnDefinitions _columnDefinitions; - -// private RowDefinitions _rowDefinitions; - -// static Grid() -// { -// AffectsParentMeasure(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty); -// IsSharedSizeScopeProperty.Changed.AddClassHandler(IsSharedSizeScopeChanged); -// } - -// public Grid() -// { -// this.AttachedToVisualTree += Grid_AttachedToVisualTree; -// this.DetachedFromVisualTree += Grid_DetachedFromVisualTree; -// } - -// /// -// /// Gets or sets the columns definitions for the grid. -// /// -// public ColumnDefinitions ColumnDefinitions -// { -// get -// { -// if (_columnDefinitions == null) -// { -// ColumnDefinitions = new ColumnDefinitions(); -// } - -// return _columnDefinitions; -// } - -// set -// { -// if (_columnDefinitions != null) -// { -// throw new NotSupportedException("Reassigning ColumnDefinitions not yet implemented."); -// } - -// _columnDefinitions = value; -// _columnDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); -// _columnDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); -// } -// } - -// /// -// /// Gets or sets the row definitions for the grid. -// /// -// public RowDefinitions RowDefinitions -// { -// get -// { -// if (_rowDefinitions == null) -// { -// RowDefinitions = new RowDefinitions(); -// } - -// return _rowDefinitions; -// } - -// set -// { -// if (_rowDefinitions != null) -// { -// throw new NotSupportedException("Reassigning RowDefinitions not yet implemented."); -// } - -// _rowDefinitions = value; -// _rowDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); -// _rowDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); -// } -// } - -// /// -// /// Gets the value of the Column attached property for a control. -// /// -// /// The control. -// /// The control's column. -// public static int GetColumn(AvaloniaObject element) -// { -// return element.GetValue(ColumnProperty); -// } - -// /// -// /// Gets the value of the ColumnSpan attached property for a control. -// /// -// /// The control. -// /// The control's column span. -// public static int GetColumnSpan(AvaloniaObject element) -// { -// return element.GetValue(ColumnSpanProperty); -// } - -// /// -// /// Gets the value of the Row attached property for a control. -// /// -// /// The control. -// /// The control's row. -// public static int GetRow(AvaloniaObject element) -// { -// return element.GetValue(RowProperty); -// } - -// /// -// /// Gets the value of the RowSpan attached property for a control. -// /// -// /// The control. -// /// The control's row span. -// public static int GetRowSpan(AvaloniaObject element) -// { -// return element.GetValue(RowSpanProperty); -// } - - -// /// -// /// Gets the value of the IsSharedSizeScope attached property for a control. -// /// -// /// The control. -// /// The control's IsSharedSizeScope value. -// public static bool GetIsSharedSizeScope(AvaloniaObject element) -// { -// return element.GetValue(IsSharedSizeScopeProperty); -// } - -// /// -// /// Sets the value of the Column attached property for a control. -// /// -// /// The control. -// /// The column value. -// public static void SetColumn(AvaloniaObject element, int value) -// { -// element.SetValue(ColumnProperty, value); -// } - -// /// -// /// Sets the value of the ColumnSpan attached property for a control. -// /// -// /// The control. -// /// The column span value. -// public static void SetColumnSpan(AvaloniaObject element, int value) -// { -// element.SetValue(ColumnSpanProperty, value); -// } - -// /// -// /// Sets the value of the Row attached property for a control. -// /// -// /// The control. -// /// The row value. -// public static void SetRow(AvaloniaObject element, int value) -// { -// element.SetValue(RowProperty, value); -// } - -// /// -// /// Sets the value of the RowSpan attached property for a control. -// /// -// /// The control. -// /// The row span value. -// public static void SetRowSpan(AvaloniaObject element, int value) -// { -// element.SetValue(RowSpanProperty, value); -// } - -// /// -// /// Sets the value of IsSharedSizeScope property for a control. -// /// -// /// The control. -// /// The IsSharedSizeScope value. -// public static void SetIsSharedSizeScope(AvaloniaObject element, bool value) -// { -// element.SetValue(IsSharedSizeScopeProperty, value); -// } - -// /// -// /// Gets the result of the last column measurement. -// /// Use this result to reduce the arrange calculation. -// /// -// private GridLayout.MeasureResult _columnMeasureCache; - -// /// -// /// Gets the result of the last row measurement. -// /// Use this result to reduce the arrange calculation. -// /// -// private GridLayout.MeasureResult _rowMeasureCache; - -// /// -// /// Gets the row layout as of the last measure. -// /// -// private GridLayout _rowLayoutCache; - -// /// -// /// Gets the column layout as of the last measure. -// /// -// private GridLayout _columnLayoutCache; - -// /// -// /// Measures the grid. -// /// -// /// The available size. -// /// The desired size of the control. -// protected override Size MeasureOverride(Size constraint) -// { -// // Situation 1/2: -// // If the grid doesn't have any column/row definitions, it behaves like a normal panel. -// // GridLayout supports this situation but we handle this separately for performance. - -// if (ColumnDefinitions.Count == 0 && RowDefinitions.Count == 0) -// { -// var maxWidth = 0.0; -// var maxHeight = 0.0; -// foreach (var child in Children.OfType()) -// { -// child.Measure(constraint); -// maxWidth = Math.Max(maxWidth, child.DesiredSize.Width); -// maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); -// } - -// maxWidth = Math.Min(maxWidth, constraint.Width); -// maxHeight = Math.Min(maxHeight, constraint.Height); -// return new Size(maxWidth, maxHeight); -// } - -// // Situation 2/2: -// // If the grid defines some columns or rows. -// // Debug Tip: -// // - GridLayout doesn't hold any state, so you can drag the debugger execution -// // arrow back to any statements and re-run them without any side-effect. - -// var measureCache = new Dictionary(); -// var (safeColumns, safeRows) = GetSafeColumnRows(); -// var columnLayout = new GridLayout(ColumnDefinitions); -// var rowLayout = new GridLayout(RowDefinitions); -// // Note: If a child stays in a * or Auto column/row, use constraint to measure it. -// columnLayout.AppendMeasureConventions(safeColumns, child => MeasureOnce(child, constraint).Width); -// rowLayout.AppendMeasureConventions(safeRows, child => MeasureOnce(child, constraint).Height); - -// // Calculate measurement. -// var columnResult = columnLayout.Measure(constraint.Width); -// var rowResult = rowLayout.Measure(constraint.Height); - -// // Use the results of the measurement to measure the rest of the children. -// foreach (var child in Children.OfType()) -// { -// var (column, columnSpan) = safeColumns[child]; -// var (row, rowSpan) = safeRows[child]; -// var width = Enumerable.Range(column, columnSpan).Select(x => columnResult.LengthList[x]).Sum(); -// var height = Enumerable.Range(row, rowSpan).Select(x => rowResult.LengthList[x]).Sum(); - -// MeasureOnce(child, new Size(width, height)); -// } - -// // Cache the measure result and return the desired size. -// _columnMeasureCache = columnResult; -// _rowMeasureCache = rowResult; -// _rowLayoutCache = rowLayout; -// _columnLayoutCache = columnLayout; - -// if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) -// { -// _sharedSizeHost.UpdateMeasureStatus(this, rowResult, columnResult); -// } - -// return new Size(columnResult.DesiredLength, rowResult.DesiredLength); - -// // Measure each child only once. -// // If a child has been measured, it will just return the desired size. -// Size MeasureOnce(Control child, Size size) -// { -// if (measureCache.TryGetValue(child, out var desiredSize)) -// { -// return desiredSize; -// } - -// child.Measure(size); -// desiredSize = child.DesiredSize; -// measureCache[child] = desiredSize; -// return desiredSize; -// } -// } - -// /// -// /// Arranges the grid's children. -// /// -// /// The size allocated to the control. -// /// The space taken. -// protected override Size ArrangeOverride(Size finalSize) -// { -// // Situation 1/2: -// // If the grid doesn't have any column/row definitions, it behaves like a normal panel. -// // GridLayout supports this situation but we handle this separately for performance. - -// if (ColumnDefinitions.Count == 0 && RowDefinitions.Count == 0) -// { -// foreach (var child in Children.OfType()) -// { -// child.Arrange(new Rect(finalSize)); -// } - -// return finalSize; -// } - -// // Situation 2/2: -// // If the grid defines some columns or rows. -// // Debug Tip: -// // - GridLayout doesn't hold any state, so you can drag the debugger execution -// // arrow back to any statements and re-run them without any side-effect. - -// var (safeColumns, safeRows) = GetSafeColumnRows(); -// var columnLayout = _columnLayoutCache; -// var rowLayout = _rowLayoutCache; - -// var rowCache = _rowMeasureCache; -// var columnCache = _columnMeasureCache; - -// if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) -// { -// (rowCache, columnCache) = _sharedSizeHost.HandleArrange(this, _rowMeasureCache, _columnMeasureCache); +// 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; +using System.Linq; +using System.Reactive.Linq; +using System.Runtime.CompilerServices; +using Avalonia.Collections; +using Avalonia.Controls.Utils; +using Avalonia.VisualTree; +using JetBrains.Annotations; + +namespace Avalonia.Controls +{ + /// + /// Lays out child controls according to a grid. + /// + public class Grid : Panel + { + /// + /// Defines the Column attached property. + /// + public static readonly AttachedProperty ColumnProperty = + AvaloniaProperty.RegisterAttached( + "Column", + validate: ValidateColumn); + + /// + /// Defines the ColumnSpan attached property. + /// + public static readonly AttachedProperty ColumnSpanProperty = + AvaloniaProperty.RegisterAttached("ColumnSpan", 1); + + /// + /// Defines the Row attached property. + /// + public static readonly AttachedProperty RowProperty = + AvaloniaProperty.RegisterAttached( + "Row", + validate: ValidateRow); + + /// + /// Defines the RowSpan attached property. + /// + public static readonly AttachedProperty RowSpanProperty = + AvaloniaProperty.RegisterAttached("RowSpan", 1); + + public static readonly AttachedProperty IsSharedSizeScopeProperty = + AvaloniaProperty.RegisterAttached("IsSharedSizeScope", false); + + protected override void OnMeasureInvalidated() + { + base.OnMeasureInvalidated(); + _sharedSizeHost?.InvalidateMeasure(this); + } + + private SharedSizeScopeHost _sharedSizeHost; + + /// + /// Defines the SharedSizeScopeHost private property. + /// The ampersands are used to make accessing the property via xaml inconvenient. + /// + internal static readonly AttachedProperty s_sharedSizeScopeHostProperty = + AvaloniaProperty.RegisterAttached("&&SharedSizeScopeHost"); + + private ColumnDefinitions _columnDefinitions; + + private RowDefinitions _rowDefinitions; + + static Grid() + { + AffectsParentMeasure(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty); + IsSharedSizeScopeProperty.Changed.AddClassHandler(IsSharedSizeScopeChanged); + } + + public Grid() + { + this.AttachedToVisualTree += Grid_AttachedToVisualTree; + this.DetachedFromVisualTree += Grid_DetachedFromVisualTree; + } + + /// + /// Gets or sets the columns definitions for the grid. + /// + public ColumnDefinitions ColumnDefinitions + { + get + { + if (_columnDefinitions == null) + { + ColumnDefinitions = new ColumnDefinitions(); + } + + return _columnDefinitions; + } + + set + { + if (_columnDefinitions != null) + { + throw new NotSupportedException("Reassigning ColumnDefinitions not yet implemented."); + } + + _columnDefinitions = value; + _columnDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); + _columnDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); + } + } + + /// + /// Gets or sets the row definitions for the grid. + /// + public RowDefinitions RowDefinitions + { + get + { + if (_rowDefinitions == null) + { + RowDefinitions = new RowDefinitions(); + } + + return _rowDefinitions; + } + + set + { + if (_rowDefinitions != null) + { + throw new NotSupportedException("Reassigning RowDefinitions not yet implemented."); + } + + _rowDefinitions = value; + _rowDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); + _rowDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); + } + } + + /// + /// Gets the value of the Column attached property for a control. + /// + /// The control. + /// The control's column. + public static int GetColumn(AvaloniaObject element) + { + return element.GetValue(ColumnProperty); + } + + /// + /// Gets the value of the ColumnSpan attached property for a control. + /// + /// The control. + /// The control's column span. + public static int GetColumnSpan(AvaloniaObject element) + { + return element.GetValue(ColumnSpanProperty); + } + + /// + /// Gets the value of the Row attached property for a control. + /// + /// The control. + /// The control's row. + public static int GetRow(AvaloniaObject element) + { + return element.GetValue(RowProperty); + } + + /// + /// Gets the value of the RowSpan attached property for a control. + /// + /// The control. + /// The control's row span. + public static int GetRowSpan(AvaloniaObject element) + { + return element.GetValue(RowSpanProperty); + } + + + /// + /// Gets the value of the IsSharedSizeScope attached property for a control. + /// + /// The control. + /// The control's IsSharedSizeScope value. + public static bool GetIsSharedSizeScope(AvaloniaObject element) + { + return element.GetValue(IsSharedSizeScopeProperty); + } + + /// + /// Sets the value of the Column attached property for a control. + /// + /// The control. + /// The column value. + public static void SetColumn(AvaloniaObject element, int value) + { + element.SetValue(ColumnProperty, value); + } + + /// + /// Sets the value of the ColumnSpan attached property for a control. + /// + /// The control. + /// The column span value. + public static void SetColumnSpan(AvaloniaObject element, int value) + { + element.SetValue(ColumnSpanProperty, value); + } + + /// + /// Sets the value of the Row attached property for a control. + /// + /// The control. + /// The row value. + public static void SetRow(AvaloniaObject element, int value) + { + element.SetValue(RowProperty, value); + } + + /// + /// Sets the value of the RowSpan attached property for a control. + /// + /// The control. + /// The row span value. + public static void SetRowSpan(AvaloniaObject element, int value) + { + element.SetValue(RowSpanProperty, value); + } + + /// + /// Sets the value of IsSharedSizeScope property for a control. + /// + /// The control. + /// The IsSharedSizeScope value. + public static void SetIsSharedSizeScope(AvaloniaObject element, bool value) + { + element.SetValue(IsSharedSizeScopeProperty, value); + } + + /// + /// Gets the result of the last column measurement. + /// Use this result to reduce the arrange calculation. + /// + private GridLayout.MeasureResult _columnMeasureCache; + + /// + /// Gets the result of the last row measurement. + /// Use this result to reduce the arrange calculation. + /// + private GridLayout.MeasureResult _rowMeasureCache; + + /// + /// Gets the row layout as of the last measure. + /// + private GridLayout _rowLayoutCache; + + /// + /// Gets the column layout as of the last measure. + /// + private GridLayout _columnLayoutCache; + + /// + /// Measures the grid. + /// + /// The available size. + /// The desired size of the control. + protected override Size MeasureOverride(Size constraint) + { + // Situation 1/2: + // If the grid doesn't have any column/row definitions, it behaves like a normal panel. + // GridLayout supports this situation but we handle this separately for performance. + + if (ColumnDefinitions.Count == 0 && RowDefinitions.Count == 0) + { + var maxWidth = 0.0; + var maxHeight = 0.0; + foreach (var child in Children.OfType()) + { + child.Measure(constraint); + maxWidth = Math.Max(maxWidth, child.DesiredSize.Width); + maxHeight = Math.Max(maxHeight, child.DesiredSize.Height); + } + + maxWidth = Math.Min(maxWidth, constraint.Width); + maxHeight = Math.Min(maxHeight, constraint.Height); + return new Size(maxWidth, maxHeight); + } + + // Situation 2/2: + // If the grid defines some columns or rows. + // Debug Tip: + // - GridLayout doesn't hold any state, so you can drag the debugger execution + // arrow back to any statements and re-run them without any side-effect. + + var measureCache = new Dictionary(); + var (safeColumns, safeRows) = GetSafeColumnRows(); + var columnLayout = new GridLayout(ColumnDefinitions); + var rowLayout = new GridLayout(RowDefinitions); + // Note: If a child stays in a * or Auto column/row, use constraint to measure it. + columnLayout.AppendMeasureConventions(safeColumns, child => MeasureOnce(child, constraint).Width); + rowLayout.AppendMeasureConventions(safeRows, child => MeasureOnce(child, constraint).Height); + + // Calculate measurement. + var columnResult = columnLayout.Measure(constraint.Width); + var rowResult = rowLayout.Measure(constraint.Height); + + // Use the results of the measurement to measure the rest of the children. + foreach (var child in Children.OfType()) + { + var (column, columnSpan) = safeColumns[child]; + var (row, rowSpan) = safeRows[child]; + var width = Enumerable.Range(column, columnSpan).Select(x => columnResult.LengthList[x]).Sum(); + var height = Enumerable.Range(row, rowSpan).Select(x => rowResult.LengthList[x]).Sum(); + + MeasureOnce(child, new Size(width, height)); + } + + // Cache the measure result and return the desired size. + _columnMeasureCache = columnResult; + _rowMeasureCache = rowResult; + _rowLayoutCache = rowLayout; + _columnLayoutCache = columnLayout; + + if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) + { + _sharedSizeHost.UpdateMeasureStatus(this, rowResult, columnResult); + } + + return new Size(columnResult.DesiredLength, rowResult.DesiredLength); + + // Measure each child only once. + // If a child has been measured, it will just return the desired size. + Size MeasureOnce(Control child, Size size) + { + if (measureCache.TryGetValue(child, out var desiredSize)) + { + return desiredSize; + } + + child.Measure(size); + desiredSize = child.DesiredSize; + measureCache[child] = desiredSize; + return desiredSize; + } + } + + /// + /// Arranges the grid's children. + /// + /// The size allocated to the control. + /// The space taken. + protected override Size ArrangeOverride(Size finalSize) + { + // Situation 1/2: + // If the grid doesn't have any column/row definitions, it behaves like a normal panel. + // GridLayout supports this situation but we handle this separately for performance. + + if (ColumnDefinitions.Count == 0 && RowDefinitions.Count == 0) + { + foreach (var child in Children.OfType()) + { + child.Arrange(new Rect(finalSize)); + } + + return finalSize; + } + + // Situation 2/2: + // If the grid defines some columns or rows. + // Debug Tip: + // - GridLayout doesn't hold any state, so you can drag the debugger execution + // arrow back to any statements and re-run them without any side-effect. + + var (safeColumns, safeRows) = GetSafeColumnRows(); + var columnLayout = _columnLayoutCache; + var rowLayout = _rowLayoutCache; + + var rowCache = _rowMeasureCache; + var columnCache = _columnMeasureCache; + + if (_sharedSizeHost?.ParticipatesInScope(this) ?? false) + { + (rowCache, columnCache) = _sharedSizeHost.HandleArrange(this, _rowMeasureCache, _columnMeasureCache); -// rowCache = rowLayout.Measure(finalSize.Height, rowCache.LeanLengthList); -// columnCache = columnLayout.Measure(finalSize.Width, columnCache.LeanLengthList); -// } - -// // Calculate for arrange result. -// var columnResult = columnLayout.Arrange(finalSize.Width, columnCache); -// var rowResult = rowLayout.Arrange(finalSize.Height, rowCache); -// // Arrange the children. -// foreach (var child in Children.OfType()) -// { -// var (column, columnSpan) = safeColumns[child]; -// var (row, rowSpan) = safeRows[child]; -// var x = Enumerable.Range(0, column).Sum(c => columnResult.LengthList[c]); -// var y = Enumerable.Range(0, row).Sum(r => rowResult.LengthList[r]); -// var width = Enumerable.Range(column, columnSpan).Sum(c => columnResult.LengthList[c]); -// var height = Enumerable.Range(row, rowSpan).Sum(r => rowResult.LengthList[r]); -// child.Arrange(new Rect(x, y, width, height)); -// } - -// // Assign the actual width. -// for (var i = 0; i < ColumnDefinitions.Count; i++) -// { -// ColumnDefinitions[i].ActualWidth = columnResult.LengthList[i]; -// } - -// // Assign the actual height. -// for (var i = 0; i < RowDefinitions.Count; i++) -// { -// RowDefinitions[i].ActualHeight = rowResult.LengthList[i]; -// } - -// // Return the render size. -// return finalSize; -// } - -// /// -// /// Tests whether this grid belongs to a shared size scope. -// /// -// /// True if the grid is registered in a shared size scope. -// internal bool HasSharedSizeScope() -// { -// return _sharedSizeHost != null; -// } - -// /// -// /// Called when the SharedSizeScope for a given grid has changed. -// /// Unregisters the grid from it's current scope and finds a new one (if any) -// /// -// /// -// /// This method, while not efficient, correctly handles nested scopes, with any order of scope changes. -// /// -// internal void SharedScopeChanged() -// { -// _sharedSizeHost?.UnegisterGrid(this); - -// _sharedSizeHost = null; -// var scope = this.GetVisualAncestors().OfType() -// .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); - -// if (scope != null) -// { -// _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); -// _sharedSizeHost.RegisterGrid(this); -// } - -// InvalidateMeasure(); -// } - -// /// -// /// Callback when a grid is attached to the visual tree. Finds the innermost SharedSizeScope and registers the grid -// /// in it. -// /// -// /// The source of the event. -// /// The event arguments. -// private void Grid_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) -// { -// var scope = -// new Control[] { this }.Concat(this.GetVisualAncestors().OfType()) -// .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); - -// if (_sharedSizeHost != null) -// throw new AvaloniaInternalException("Shared size scope already present when attaching to visual tree!"); - -// if (scope != null) -// { -// _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); -// _sharedSizeHost.RegisterGrid(this); -// } -// } - -// /// -// /// Callback when a grid is detached from the visual tree. Unregisters the grid from its SharedSizeScope if any. -// /// -// /// The source of the event. -// /// The event arguments. -// private void Grid_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) -// { -// _sharedSizeHost?.UnegisterGrid(this); -// _sharedSizeHost = null; -// } - - -// /// -// /// Get the safe column/columnspan and safe row/rowspan. -// /// This method ensures that none of the children has a column/row outside the bounds of the definitions. -// /// -// [Pure] -// private (Dictionary safeColumns, -// Dictionary safeRows) GetSafeColumnRows() -// { -// var columnCount = ColumnDefinitions.Count; -// var rowCount = RowDefinitions.Count; -// columnCount = columnCount == 0 ? 1 : columnCount; -// rowCount = rowCount == 0 ? 1 : rowCount; -// var safeColumns = Children.OfType().ToDictionary(child => child, -// child => GetSafeSpan(columnCount, GetColumn(child), GetColumnSpan(child))); -// var safeRows = Children.OfType().ToDictionary(child => child, -// child => GetSafeSpan(rowCount, GetRow(child), GetRowSpan(child))); -// return (safeColumns, safeRows); -// } - -// /// -// /// Gets the safe row/column and rowspan/columnspan for a specified range. -// /// The user may assign row/column properties outside the bounds of the row/column count, this method coerces them inside. -// /// -// /// The row or column count. -// /// The row or column that the user assigned. -// /// The rowspan or columnspan that the user assigned. -// /// The safe row/column and rowspan/columnspan. -// [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] -// private static (int index, int span) GetSafeSpan(int length, int userIndex, int userSpan) -// { -// var index = userIndex; -// var span = userSpan; - -// if (index < 0) -// { -// span = index + span; -// index = 0; -// } - -// if (span <= 0) -// { -// span = 1; -// } - -// if (userIndex >= length) -// { -// index = length - 1; -// span = 1; -// } -// else if (userIndex + userSpan > length) -// { -// span = length - userIndex; -// } - -// return (index, span); -// } - -// private static int ValidateColumn(AvaloniaObject o, int value) -// { -// if (value < 0) -// { -// throw new ArgumentException("Invalid Grid.Column value."); -// } - -// return value; -// } - -// private static int ValidateRow(AvaloniaObject o, int value) -// { -// if (value < 0) -// { -// throw new ArgumentException("Invalid Grid.Row value."); -// } - -// return value; -// } - -// /// -// /// Called when the value of changes for a control. -// /// -// /// The control that triggered the change. -// /// Change arguments. -// private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) -// { -// var shouldDispose = (arg2.OldValue is bool d) && d; -// if (shouldDispose) -// { -// var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; -// if (host == null) -// throw new AvaloniaInternalException("SharedScopeHost wasn't set when IsSharedSizeScope was true!"); -// host.Dispose(); -// source.ClearValue(s_sharedSizeScopeHostProperty); -// } - -// var shouldAssign = (arg2.NewValue is bool a) && a; -// if (shouldAssign) -// { -// if (source.GetValue(s_sharedSizeScopeHostProperty) != null) -// throw new AvaloniaInternalException("SharedScopeHost was already set when IsSharedSizeScope is only now being set to true!"); -// source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost()); -// } - -// // if the scope has changed, notify the descendant grids that they need to update. -// if (source.GetVisualRoot() != null && shouldAssign || shouldDispose) -// { -// var participatingGrids = new[] { source }.Concat(source.GetVisualDescendants()).OfType(); - -// foreach (var grid in participatingGrids) -// grid.SharedScopeChanged(); - -// } -// } -// } -// } + rowCache = rowLayout.Measure(finalSize.Height, rowCache.LeanLengthList); + columnCache = columnLayout.Measure(finalSize.Width, columnCache.LeanLengthList); + } + + // Calculate for arrange result. + var columnResult = columnLayout.Arrange(finalSize.Width, columnCache); + var rowResult = rowLayout.Arrange(finalSize.Height, rowCache); + // Arrange the children. + foreach (var child in Children.OfType()) + { + var (column, columnSpan) = safeColumns[child]; + var (row, rowSpan) = safeRows[child]; + var x = Enumerable.Range(0, column).Sum(c => columnResult.LengthList[c]); + var y = Enumerable.Range(0, row).Sum(r => rowResult.LengthList[r]); + var width = Enumerable.Range(column, columnSpan).Sum(c => columnResult.LengthList[c]); + var height = Enumerable.Range(row, rowSpan).Sum(r => rowResult.LengthList[r]); + child.Arrange(new Rect(x, y, width, height)); + } + + // Assign the actual width. + for (var i = 0; i < ColumnDefinitions.Count; i++) + { + ColumnDefinitions[i].ActualWidth = columnResult.LengthList[i]; + } + + // Assign the actual height. + for (var i = 0; i < RowDefinitions.Count; i++) + { + RowDefinitions[i].ActualHeight = rowResult.LengthList[i]; + } + + // Return the render size. + return finalSize; + } + + /// + /// Tests whether this grid belongs to a shared size scope. + /// + /// True if the grid is registered in a shared size scope. + internal bool HasSharedSizeScope() + { + return _sharedSizeHost != null; + } + + /// + /// Called when the SharedSizeScope for a given grid has changed. + /// Unregisters the grid from it's current scope and finds a new one (if any) + /// + /// + /// This method, while not efficient, correctly handles nested scopes, with any order of scope changes. + /// + internal void SharedScopeChanged() + { + _sharedSizeHost?.UnegisterGrid(this); + + _sharedSizeHost = null; + var scope = this.GetVisualAncestors().OfType() + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + if (scope != null) + { + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); + } + + InvalidateMeasure(); + } + + /// + /// Callback when a grid is attached to the visual tree. Finds the innermost SharedSizeScope and registers the grid + /// in it. + /// + /// The source of the event. + /// The event arguments. + private void Grid_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + var scope = + new Control[] { this }.Concat(this.GetVisualAncestors().OfType()) + .FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty)); + + if (_sharedSizeHost != null) + throw new AvaloniaInternalException("Shared size scope already present when attaching to visual tree!"); + + if (scope != null) + { + _sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty); + _sharedSizeHost.RegisterGrid(this); + } + } + + /// + /// Callback when a grid is detached from the visual tree. Unregisters the grid from its SharedSizeScope if any. + /// + /// The source of the event. + /// The event arguments. + private void Grid_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + _sharedSizeHost?.UnegisterGrid(this); + _sharedSizeHost = null; + } + + + /// + /// Get the safe column/columnspan and safe row/rowspan. + /// This method ensures that none of the children has a column/row outside the bounds of the definitions. + /// + [Pure] + private (Dictionary safeColumns, + Dictionary safeRows) GetSafeColumnRows() + { + var columnCount = ColumnDefinitions.Count; + var rowCount = RowDefinitions.Count; + columnCount = columnCount == 0 ? 1 : columnCount; + rowCount = rowCount == 0 ? 1 : rowCount; + var safeColumns = Children.OfType().ToDictionary(child => child, + child => GetSafeSpan(columnCount, GetColumn(child), GetColumnSpan(child))); + var safeRows = Children.OfType().ToDictionary(child => child, + child => GetSafeSpan(rowCount, GetRow(child), GetRowSpan(child))); + return (safeColumns, safeRows); + } + + /// + /// Gets the safe row/column and rowspan/columnspan for a specified range. + /// The user may assign row/column properties outside the bounds of the row/column count, this method coerces them inside. + /// + /// The row or column count. + /// The row or column that the user assigned. + /// The rowspan or columnspan that the user assigned. + /// The safe row/column and rowspan/columnspan. + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (int index, int span) GetSafeSpan(int length, int userIndex, int userSpan) + { + var index = userIndex; + var span = userSpan; + + if (index < 0) + { + span = index + span; + index = 0; + } + + if (span <= 0) + { + span = 1; + } + + if (userIndex >= length) + { + index = length - 1; + span = 1; + } + else if (userIndex + userSpan > length) + { + span = length - userIndex; + } + + return (index, span); + } + + private static int ValidateColumn(AvaloniaObject o, int value) + { + if (value < 0) + { + throw new ArgumentException("Invalid Grid.Column value."); + } + + return value; + } + + private static int ValidateRow(AvaloniaObject o, int value) + { + if (value < 0) + { + throw new ArgumentException("Invalid Grid.Row value."); + } + + return value; + } + + /// + /// Called when the value of changes for a control. + /// + /// The control that triggered the change. + /// Change arguments. + private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2) + { + var shouldDispose = (arg2.OldValue is bool d) && d; + if (shouldDispose) + { + var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost; + if (host == null) + throw new AvaloniaInternalException("SharedScopeHost wasn't set when IsSharedSizeScope was true!"); + host.Dispose(); + source.ClearValue(s_sharedSizeScopeHostProperty); + } + + var shouldAssign = (arg2.NewValue is bool a) && a; + if (shouldAssign) + { + if (source.GetValue(s_sharedSizeScopeHostProperty) != null) + throw new AvaloniaInternalException("SharedScopeHost was already set when IsSharedSizeScope is only now being set to true!"); + source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost()); + } + + // if the scope has changed, notify the descendant grids that they need to update. + if (source.GetVisualRoot() != null && shouldAssign || shouldDispose) + { + var participatingGrids = new[] { source }.Concat(source.GetVisualDescendants()).OfType(); + + foreach (var grid in participatingGrids) + grid.SharedScopeChanged(); + } + } + } +}