|
|
|
@ -13,6 +13,12 @@ using Avalonia.VisualTree; |
|
|
|
|
|
|
|
namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Shared size scope implementation.
|
|
|
|
/// Shares the size information between participating grids.
|
|
|
|
/// An instance of this class is attached to every <see cref="Control"/> that has its
|
|
|
|
/// IsSharedSizeScope property set to true.
|
|
|
|
/// </summary>
|
|
|
|
internal sealed class SharedSizeScopeHost : IDisposable |
|
|
|
{ |
|
|
|
private enum MeasurementState |
|
|
|
@ -22,6 +28,12 @@ namespace Avalonia.Controls |
|
|
|
Cached |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Class containing the measured rows/columns for a single grid.
|
|
|
|
/// Monitors changes to the row/column collections as well as the SharedSizeGroup changes
|
|
|
|
/// for the individual items in those collections.
|
|
|
|
/// Notifies the <see cref="SharedSizeScopeHost"/> of SharedSizeGroup changes.
|
|
|
|
/// </summary>
|
|
|
|
private sealed class MeasurementCache : IDisposable |
|
|
|
{ |
|
|
|
readonly CompositeDisposable _subscriptions; |
|
|
|
@ -129,6 +141,12 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Updates the Results collection with Grid Measure results.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="rowResult">Result of the GridLayout.Measure method for the RowDefinitions in the grid.</param>
|
|
|
|
/// <param name="columnResult">Result of the GridLayout.Measure method for the ColumnDefinitions in the grid.</param>
|
|
|
|
public void UpdateMeasureResult(GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) |
|
|
|
{ |
|
|
|
MeasurementState = MeasurementState.Cached; |
|
|
|
@ -145,9 +163,16 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Clears the measurement cache, in preparation for the Measure pass.
|
|
|
|
/// </summary>
|
|
|
|
public void InvalidateMeasure() |
|
|
|
{ |
|
|
|
var newItems = new List<MeasurementResult>(); |
|
|
|
var oldItems = new List<MeasurementResult>(); |
|
|
|
|
|
|
|
MeasurementState = MeasurementState.Invalidated; |
|
|
|
|
|
|
|
Results.ForEach(r => |
|
|
|
{ |
|
|
|
r.MeasuredResult = double.NaN; |
|
|
|
@ -155,18 +180,38 @@ namespace Avalonia.Controls |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Clears the <see cref="IObservable{T}"/> subscriptions.
|
|
|
|
/// </summary>
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
_subscriptions.Dispose(); |
|
|
|
_groupChanged.OnCompleted(); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the <see cref="Grid"/> for which this cache has been created.
|
|
|
|
/// </summary>
|
|
|
|
public Grid Grid { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the <see cref="MeasurementState"/> of this cache.
|
|
|
|
/// </summary>
|
|
|
|
public MeasurementState MeasurementState { get; private set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the list of <see cref="MeasurementResult"/> instances.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// The list is a 1-1 map of the concatenation of RowDefinitions and ColumnDefinitions
|
|
|
|
/// </remarks>
|
|
|
|
public List<MeasurementResult> Results { get; private set; } |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Class containing the Measure result for a single Row/Column in a grid.
|
|
|
|
/// </summary>
|
|
|
|
private class MeasurementResult |
|
|
|
{ |
|
|
|
public MeasurementResult(Grid owningGrid, DefinitionBase definition) |
|
|
|
@ -176,12 +221,35 @@ namespace Avalonia.Controls |
|
|
|
MeasuredResult = double.NaN; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the <see cref="RowDefinition"/>/<see cref="ColumnDefinition"/> related to this <see cref="MeasurementResult"/>
|
|
|
|
/// </summary>
|
|
|
|
public DefinitionBase Definition { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the actual result of the Measure operation for this column.
|
|
|
|
/// </summary>
|
|
|
|
public double MeasuredResult { get; set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the Minimum constraint for a Row/Column - relevant for star Rows/Columns in unconstrained grids.
|
|
|
|
/// </summary>
|
|
|
|
public double MinLength { get; set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the <see cref="Group"/> that this result belongs to.
|
|
|
|
/// </summary>
|
|
|
|
public Group SizeGroup { get; set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the Grid that is the parent of the Row/Column
|
|
|
|
/// </summary>
|
|
|
|
public Grid OwningGrid { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Calculates the effective length that this Row/Column wishes to enforce in the SharedSizeGroup.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple of length and the priority in the shared size group.</returns>
|
|
|
|
public (double length, int priority) GetPriorityLength() |
|
|
|
{ |
|
|
|
var length = (Definition as ColumnDefinition)?.Width ?? ((RowDefinition)Definition).Height; |
|
|
|
@ -196,12 +264,24 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Visitor class used to gather the final length for a given SharedSizeGroup.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// The values are applied according to priorities defined in <see cref="MeasurementResult.GetPriorityLength"/>.
|
|
|
|
/// </remarks>
|
|
|
|
private class LentgthGatherer |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Gets the final Length to be applied to every Row/Column in a SharedSizeGroup
|
|
|
|
/// </summary>
|
|
|
|
public double Length { get; private set; } |
|
|
|
private int gatheredPriority = 6; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Visits the <paramref name="result"/> applying the result of <see cref="MeasurementResult.GetPriorityLength"/> to its internal cache.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="result">The <see cref="MeasurementResult"/> instance to visit.</param>
|
|
|
|
public void Visit(MeasurementResult result) |
|
|
|
{ |
|
|
|
var (length, priority) = result.GetPriorityLength(); |
|
|
|
@ -221,12 +301,17 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Representation of a SharedSizeGroup, containing Rows/Columns with the same SharedSizeGroup property value.
|
|
|
|
/// </summary>
|
|
|
|
private class Group |
|
|
|
{ |
|
|
|
private double? cachedResult; |
|
|
|
private List<MeasurementResult> _results = new List<MeasurementResult>(); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the name of the SharedSizeGroup.
|
|
|
|
/// </summary>
|
|
|
|
public string Name { get; } |
|
|
|
|
|
|
|
public Group(string name) |
|
|
|
@ -234,17 +319,29 @@ namespace Avalonia.Controls |
|
|
|
Name = name; |
|
|
|
} |
|
|
|
|
|
|
|
public bool IsFixed { get; set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the collection of the <see cref="MeasurementResult"/> instances.
|
|
|
|
/// </summary>
|
|
|
|
public IReadOnlyList<MeasurementResult> Results => _results; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the final, calculated length for all Rows/Columns in the SharedSizeGroup.
|
|
|
|
/// </summary>
|
|
|
|
public double CalculatedLength => (cachedResult ?? (cachedResult = Gather())).Value; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Clears the previously cached result in preparation for measurement.
|
|
|
|
/// </summary>
|
|
|
|
public void Reset() |
|
|
|
{ |
|
|
|
cachedResult = null; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Ads a measurement result to this group and sets it's <see cref="MeasurementResult.SizeGroup"/> property
|
|
|
|
/// to this instance.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="result">The <see cref="MeasurementResult"/> to include in this group.</param>
|
|
|
|
public void Add(MeasurementResult result) |
|
|
|
{ |
|
|
|
if (_results.Contains(result)) |
|
|
|
@ -255,6 +352,10 @@ namespace Avalonia.Controls |
|
|
|
_results.Add(result); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Removes the measurement result from this group and clears its <see cref="MeasurementResult.SizeGroup"/> value.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="result">The <see cref="MeasurementResult"/> to clear.</param>
|
|
|
|
public void Remove(MeasurementResult result) |
|
|
|
{ |
|
|
|
if (!_results.Contains(result)) |
|
|
|
@ -275,30 +376,64 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private readonly AvaloniaList<MeasurementCache> _measurementCaches; |
|
|
|
|
|
|
|
private readonly AvaloniaList<MeasurementCache> _measurementCaches = new AvaloniaList<MeasurementCache>(); |
|
|
|
private readonly Dictionary<string, Group> _groups = new Dictionary<string, Group>(); |
|
|
|
private bool _invalidating; |
|
|
|
|
|
|
|
public SharedSizeScopeHost(Control scope) |
|
|
|
/// <summary>
|
|
|
|
/// Removes the SharedSizeScope and notifies all affected grids of the change.
|
|
|
|
/// </summary>
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
_measurementCaches = GetParticipatingGrids(scope); |
|
|
|
while (_measurementCaches.Any()) |
|
|
|
_measurementCaches[0].Grid.SharedScopeChanged(); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (var cache in _measurementCaches) |
|
|
|
{ |
|
|
|
cache.Grid.InvalidateMeasure(); |
|
|
|
AddGridToScopes(cache); |
|
|
|
/// <summary>
|
|
|
|
/// Registers the grid in this SharedSizeScope, to be called when the grid is added to the visual tree.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="toAdd">The <see cref="Grid"/> to add to this scope.</param>
|
|
|
|
internal void RegisterGrid(Grid toAdd) |
|
|
|
{ |
|
|
|
if (_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))) |
|
|
|
throw new AvaloniaInternalException("SharedSizeScopeHost: tried to register a grid twice!"); |
|
|
|
|
|
|
|
} |
|
|
|
var cache = new MeasurementCache(toAdd); |
|
|
|
_measurementCaches.Add(cache); |
|
|
|
AddGridToScopes(cache); |
|
|
|
} |
|
|
|
|
|
|
|
void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) |
|
|
|
/// <summary>
|
|
|
|
/// Removes the registration for a grid in this SharedSizeScope.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="toRemove">The <see cref="Grid"/> to remove.</param>
|
|
|
|
internal void UnegisterGrid(Grid toRemove) |
|
|
|
{ |
|
|
|
RemoveFromGroup(change.oldName, change.result); |
|
|
|
AddToGroup(change.newName, change.result); |
|
|
|
var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); |
|
|
|
if (cache == null) |
|
|
|
throw new AvaloniaInternalException("SharedSizeScopeHost: tried to unregister a grid that wasn't registered before!"); |
|
|
|
|
|
|
|
_measurementCaches.Remove(cache); |
|
|
|
RemoveGridFromScopes(cache); |
|
|
|
cache.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
private bool _invalidating; |
|
|
|
/// <summary>
|
|
|
|
/// Helper method to check if a grid needs to forward its Mesure results to, and requrest Arrange results from this scope.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="toCheck">The <see cref="Grid"/> that should be checked.</param>
|
|
|
|
/// <returns>True if the grid should forward its calls.</returns>
|
|
|
|
internal bool ParticipatesInScope(Grid toCheck) |
|
|
|
{ |
|
|
|
return _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toCheck)) |
|
|
|
?.Results.Any(r => r.SizeGroup != null) ?? false; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Notifies the SharedSizeScope that a grid had requested its measurement to be invalidated.
|
|
|
|
/// Forwards the same call to all affected grids in this scope.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="grid">The <see cref="Grid"/> that had it's Measure invalidated.</param>
|
|
|
|
internal void InvalidateMeasure(Grid grid) |
|
|
|
{ |
|
|
|
// prevent stack overflow
|
|
|
|
@ -311,6 +446,40 @@ namespace Avalonia.Controls |
|
|
|
_invalidating = false; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Updates the measurement cache with the results of the <paramref name="grid"/> measurement pass.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="grid">The <see cref="Grid"/> that has been measured.</param>
|
|
|
|
/// <param name="rowResult">Measurement result for the grid's <see cref="RowDefinitions"/></param>
|
|
|
|
/// <param name="columnResult">Measurement result for the grid's <see cref="ColumnDefinitions"/></param>
|
|
|
|
internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) |
|
|
|
{ |
|
|
|
var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); |
|
|
|
if (cache == null) |
|
|
|
throw new AvaloniaInternalException("SharedSizeScopeHost: Attempted to update measurement status for a grid that wasn't registered!"); |
|
|
|
|
|
|
|
cache.UpdateMeasureResult(rowResult, columnResult); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Calculates the measurement result including the impact of any SharedSizeGroups that might affect this grid.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="grid">The <see cref="Grid"/> that is being Arranged</param>
|
|
|
|
/// <param name="rowResult">The <paramref name="grid"/>'s cached measurement result.</param>
|
|
|
|
/// <param name="columnResult">The <paramref name="grid"/>'s cached measurement result.</param>
|
|
|
|
/// <returns>Row and column measurement result updated with the SharedSizeScope constraints.</returns>
|
|
|
|
internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) |
|
|
|
{ |
|
|
|
return ( |
|
|
|
Arrange(grid.RowDefinitions, rowResult), |
|
|
|
Arrange(grid.ColumnDefinitions, columnResult) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Invalidates the measure of all grids affected by the SharedSizeGroups contained within.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="grid">The <see cref="Grid"/> that is being invalidated.</param>
|
|
|
|
private void InvalidateMeasureImpl(Grid grid) |
|
|
|
{ |
|
|
|
var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); |
|
|
|
@ -323,6 +492,10 @@ namespace Avalonia.Controls |
|
|
|
if (cache.MeasurementState == MeasurementState.Invalidated) |
|
|
|
return; |
|
|
|
|
|
|
|
// we won't calculate, so we should not invalidate.
|
|
|
|
if (!ParticipatesInScope(grid)) |
|
|
|
return; |
|
|
|
|
|
|
|
cache.InvalidateMeasure(); |
|
|
|
|
|
|
|
// maybe there is a condition to only call arrange on some of the calls?
|
|
|
|
@ -346,16 +519,24 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal void UpdateMeasureStatus(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) |
|
|
|
/// <summary>
|
|
|
|
/// <see cref="IObserver{T}"/> callback notifying the scope that a <see cref="MeasurementResult"/> has changed its
|
|
|
|
/// SharedSizeGroup
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="change">Old and New name (either can be null) of the SharedSizeGroup, as well as the result.</param>
|
|
|
|
private void SharedGroupChanged((string oldName, string newName, MeasurementResult result) change) |
|
|
|
{ |
|
|
|
var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, grid)); |
|
|
|
if (cache == null) |
|
|
|
throw new AvaloniaInternalException("SharedSizeScopeHost: Attempted to update measurement status for a grid that wasn't registered!"); |
|
|
|
|
|
|
|
cache.UpdateMeasureResult(rowResult, columnResult); |
|
|
|
RemoveFromGroup(change.oldName, change.result); |
|
|
|
AddToGroup(change.newName, change.result); |
|
|
|
} |
|
|
|
|
|
|
|
(List<GridLayout.LengthConvention>, List<double>, double) Arrange(IReadOnlyList<DefinitionBase> definitions, GridLayout.MeasureResult measureResult) |
|
|
|
/// <summary>
|
|
|
|
/// Handles the impact of SharedSizeGroups on the Arrange of <see cref="RowDefinitions"/>/<see cref="ColumnDefinitions"/>
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="definitions">Rows/Columns that were measured</param>
|
|
|
|
/// <param name="measureResult">The initial measurement result.</param>
|
|
|
|
/// <returns>Modified measure result</returns>
|
|
|
|
private GridLayout.MeasureResult Arrange(IReadOnlyList<DefinitionBase> definitions, GridLayout.MeasureResult measureResult) |
|
|
|
{ |
|
|
|
var conventions = measureResult.LeanLengthList.ToList(); |
|
|
|
var lengths = measureResult.LengthList.ToList(); |
|
|
|
@ -363,6 +544,8 @@ namespace Avalonia.Controls |
|
|
|
for (int i = 0; i < definitions.Count; i++) |
|
|
|
{ |
|
|
|
var definition = definitions[i]; |
|
|
|
|
|
|
|
// for empty SharedSizeGroups pass on unmodified result.
|
|
|
|
if (string.IsNullOrEmpty(definition.SharedSizeGroup)) |
|
|
|
{ |
|
|
|
desiredLength += measureResult.LengthList[i]; |
|
|
|
@ -370,7 +553,7 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
|
|
|
|
var group = _groups[definition.SharedSizeGroup]; |
|
|
|
|
|
|
|
// Length calculated over all Definitions participating in a SharedSizeGroup.
|
|
|
|
var length = group.CalculatedLength; |
|
|
|
|
|
|
|
conventions[i] = new GridLayout.LengthConvention( |
|
|
|
@ -382,33 +565,19 @@ namespace Avalonia.Controls |
|
|
|
desiredLength += length; |
|
|
|
} |
|
|
|
|
|
|
|
return (conventions, lengths, desiredLength); |
|
|
|
} |
|
|
|
|
|
|
|
internal (GridLayout.MeasureResult, GridLayout.MeasureResult) HandleArrange(Grid grid, GridLayout.MeasureResult rowResult, GridLayout.MeasureResult columnResult) |
|
|
|
{ |
|
|
|
var (rowConventions, rowLengths, rowDesiredLength) = Arrange(grid.RowDefinitions, rowResult); |
|
|
|
var (columnConventions, columnLengths, columnDesiredLength) = Arrange(grid.ColumnDefinitions, columnResult); |
|
|
|
|
|
|
|
return ( |
|
|
|
new GridLayout.MeasureResult( |
|
|
|
rowResult.ContainerLength, |
|
|
|
rowDesiredLength, |
|
|
|
rowResult.GreedyDesiredLength,//??
|
|
|
|
rowConventions, |
|
|
|
rowLengths, |
|
|
|
rowResult.MinLengths), |
|
|
|
new GridLayout.MeasureResult( |
|
|
|
columnResult.ContainerLength, |
|
|
|
columnDesiredLength, |
|
|
|
columnResult.GreedyDesiredLength, //??
|
|
|
|
columnConventions, |
|
|
|
columnLengths, |
|
|
|
columnResult.MinLengths) |
|
|
|
); |
|
|
|
return new GridLayout.MeasureResult( |
|
|
|
measureResult.ContainerLength, |
|
|
|
desiredLength, |
|
|
|
measureResult.GreedyDesiredLength,//??
|
|
|
|
conventions, |
|
|
|
lengths, |
|
|
|
measureResult.MinLengths); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Adds all measurement results for a grid to their repsective scopes.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="cache">The <see cref="MeasurementCache"/> for a grid to be added.</param>
|
|
|
|
private void AddGridToScopes(MeasurementCache cache) |
|
|
|
{ |
|
|
|
cache.GroupChanged.Subscribe(SharedGroupChanged); |
|
|
|
@ -420,6 +589,12 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Handles adding the <see cref="MeasurementResult"/> to a SharedSizeGroup.
|
|
|
|
/// Does nothing for empty SharedSizeGroups.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="scopeName">The name (can be null or empty) of the group to add the <paramref name="result"/> to.</param>
|
|
|
|
/// <param name="result">The <see cref="MeasurementResult"/> to add to a scope.</param>
|
|
|
|
private void AddToGroup(string scopeName, MeasurementResult result) |
|
|
|
{ |
|
|
|
if (string.IsNullOrEmpty(scopeName)) |
|
|
|
@ -428,16 +603,13 @@ namespace Avalonia.Controls |
|
|
|
if (!_groups.TryGetValue(scopeName, out var group)) |
|
|
|
_groups.Add(scopeName, group = new Group(scopeName)); |
|
|
|
|
|
|
|
group.IsFixed |= IsFixed(result.Definition); |
|
|
|
|
|
|
|
group.Add(result); |
|
|
|
} |
|
|
|
|
|
|
|
private bool IsFixed(DefinitionBase definition) |
|
|
|
{ |
|
|
|
return ((definition as ColumnDefinition)?.Width ?? ((RowDefinition)definition).Height).IsAbsolute; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Removes all measurement results for a grid from their respective scopes.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="cache">The <see cref="MeasurementCache"/> for a grid to be removed.</param>
|
|
|
|
private void RemoveGridFromScopes(MeasurementCache cache) |
|
|
|
{ |
|
|
|
foreach (var result in cache.Results) |
|
|
|
@ -447,6 +619,12 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Handles removing the <see cref="MeasurementResult"/> from a SharedSizeGroup.
|
|
|
|
/// Does nothing for empty SharedSizeGroups.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="scopeName">The name (can be null or empty) of the group to remove the <paramref name="result"/> from.</param>
|
|
|
|
/// <param name="result">The <see cref="MeasurementResult"/> to remove from a scope.</param>
|
|
|
|
private void RemoveFromGroup(string scopeName, MeasurementResult result) |
|
|
|
{ |
|
|
|
if (string.IsNullOrEmpty(scopeName)) |
|
|
|
@ -458,54 +636,6 @@ namespace Avalonia.Controls |
|
|
|
group.Remove(result); |
|
|
|
if (!group.Results.Any()) |
|
|
|
_groups.Remove(scopeName); |
|
|
|
else |
|
|
|
{ |
|
|
|
group.IsFixed = group.Results.Select(r => r.Definition).Any(IsFixed); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static AvaloniaList<MeasurementCache> GetParticipatingGrids(Control scope) |
|
|
|
{ |
|
|
|
var result = scope.GetVisualDescendants().OfType<Grid>(); |
|
|
|
|
|
|
|
return new AvaloniaList<MeasurementCache>( |
|
|
|
result.Where(g => g.HasSharedSizeGroups()) |
|
|
|
.Select(g => new MeasurementCache(g))); |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
foreach (var cache in _measurementCaches) |
|
|
|
{ |
|
|
|
cache.Grid.SharedScopeChanged(); |
|
|
|
cache.Dispose(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal void RegisterGrid(Grid toAdd) |
|
|
|
{ |
|
|
|
if (_measurementCaches.Any(mc => ReferenceEquals(mc.Grid, toAdd))) |
|
|
|
throw new AvaloniaInternalException("SharedSizeScopeHost: tried to register a grid twice!"); |
|
|
|
|
|
|
|
var cache = new MeasurementCache(toAdd); |
|
|
|
_measurementCaches.Add(cache); |
|
|
|
AddGridToScopes(cache); |
|
|
|
} |
|
|
|
|
|
|
|
internal void UnegisterGrid(Grid toRemove) |
|
|
|
{ |
|
|
|
var cache = _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toRemove)); |
|
|
|
if (cache == null) |
|
|
|
throw new AvaloniaInternalException("SharedSizeScopeHost: tried to unregister a grid that wasn't registered before!"); |
|
|
|
|
|
|
|
_measurementCaches.Remove(cache); |
|
|
|
RemoveGridFromScopes(cache); |
|
|
|
cache.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
internal bool ParticipatesInScope(Grid toCheck) |
|
|
|
{ |
|
|
|
return _measurementCaches.FirstOrDefault(mc => ReferenceEquals(mc.Grid, toCheck))?.Results.Any() ?? false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|