Browse Source

Revert back to WPF and start from scratch, using thepower of Hindsight~

pull/2563/head
Jumar Macato 7 years ago
parent
commit
3764e61bc9
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 6
      src/Avalonia.Controls/ColumnDefinition.cs
  2. 0
      src/Avalonia.Controls/ColumnDefinitions.cs
  3. 992
      src/Avalonia.Controls/DefinitionBase.cs
  4. 4424
      src/Avalonia.Controls/Grid.cs
  5. 339
      src/Avalonia.Controls/Grid/DefinitionBase.cs
  6. 2378
      src/Avalonia.Controls/Grid/Grid.cs
  7. 26
      src/Avalonia.Controls/Grid/GridCellCache.cs
  8. 220
      src/Avalonia.Controls/Grid/GridLength.cs
  9. 91
      src/Avalonia.Controls/Grid/GridLinesRenderer.cs
  10. 69
      src/Avalonia.Controls/Grid/GridSpanKey.cs
  11. 209
      src/Avalonia.Controls/Grid/GridSplitter.cs
  12. 17
      src/Avalonia.Controls/Grid/LayoutTimeSizeType.cs
  13. 31
      src/Avalonia.Controls/Grid/MaxRatioComparer.cs
  14. 46
      src/Avalonia.Controls/Grid/MaxRatioIndexComparer.cs
  15. 30
      src/Avalonia.Controls/Grid/MinRatioComparer.cs
  16. 46
      src/Avalonia.Controls/Grid/MinRatioIndexComparer.cs
  17. 36
      src/Avalonia.Controls/Grid/RoundingErrorIndexComparer.cs
  18. 43
      src/Avalonia.Controls/Grid/SharedSizeScope.cs
  19. 209
      src/Avalonia.Controls/Grid/SharedSizeState.cs
  20. 46
      src/Avalonia.Controls/Grid/SpanMaxDistributionOrderComparer.cs
  21. 46
      src/Avalonia.Controls/Grid/SpanPreferredDistributionOrderComparer.cs
  22. 29
      src/Avalonia.Controls/Grid/StarWeightComparer.cs
  23. 47
      src/Avalonia.Controls/Grid/StarWeightIndexComparer.cs
  24. 7
      src/Avalonia.Controls/RowDefinition.cs
  25. 0
      src/Avalonia.Controls/RowDefinitions.cs

6
src/Avalonia.Controls/Grid/ColumnDefinition.cs → src/Avalonia.Controls/ColumnDefinition.cs

@ -87,11 +87,5 @@ namespace Avalonia.Controls
get { return GetValue(WidthProperty); }
set { SetValue(WidthProperty, value); }
}
internal override GridLength UserSizeValueCache => this.Width;
internal override double UserMinSizeValueCache => this.MinWidth;
internal override double UserMaxSizeValueCache => this.MaxWidth;
}
}

0
src/Avalonia.Controls/Grid/ColumnDefinitions.cs → src/Avalonia.Controls/ColumnDefinitions.cs

992
src/Avalonia.Controls/DefinitionBase.cs

@ -0,0 +1,992 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Specs
// Grid : Grid.mht
// Size Sharing: Size Information Sharing.doc
//
// Misc
// Grid Tutorial: Grid Tutorial.mht
//
// Description: Implementation of base abstract class for ColumnDefinition
// and RowDefinition.
//
using MS.Internal;
using MS.Internal.KnownBoxes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Threading;
namespace System.Windows.Controls
{
/// <summary>
/// DefinitionBase provides core functionality used internally by Grid
/// and ColumnDefinitionCollection / RowDefinitionCollection
/// </summary>
[Localizability(LocalizationCategory.Ignore)]
public abstract class DefinitionBase : FrameworkContentElement
{
//------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
internal DefinitionBase(bool isColumnDefinition)
{
_isColumnDefinition = isColumnDefinition;
_parentIndex = -1;
}
#endregion Constructors
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region Public Properties
/// <summary>
/// SharedSizeGroup property.
/// </summary>
public string SharedSizeGroup
{
get { return (string) GetValue(SharedSizeGroupProperty); }
set { SetValue(SharedSizeGroupProperty, value); }
}
#endregion Public Properties
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
/// <summary>
/// Callback to notify about entering model tree.
/// </summary>
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);
}
}
}
}
/// <summary>
/// Callback to notify about exitting model tree.
/// </summary>
internal void OnExitParentTree()
{
_offset = 0;
if (_sharedState != null)
{
_sharedState.RemoveMember(this);
_sharedState = null;
}
}
/// <summary>
/// Performs action preparing definition to enter layout calculation mode.
/// </summary>
internal void OnBeforeLayout(Grid grid)
{
// reset layout state.
_minSize = 0;
LayoutWasUpdated = true;
// defer verification for shared definitions
if (_sharedState != null) { _sharedState.EnsureDeferredValidation(grid); }
}
/// <summary>
/// Updates min size.
/// </summary>
/// <param name="minSize">New size.</param>
internal void UpdateMinSize(double minSize)
{
_minSize = Math.Max(_minSize, minSize);
}
/// <summary>
/// Sets min size.
/// </summary>
/// <param name="minSize">New size.</param>
internal void SetMinSize(double minSize)
{
_minSize = minSize;
}
/// <summary>
/// <see cref="PropertyMetadata.PropertyChangedCallback"/>
/// </summary>
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
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();
}
}
}
}
/// <summary>
/// <see cref="DependencyProperty.ValidateValueCallback"/>
/// </summary>
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserSizePropertyValueValid(object value)
{
return (((GridLength)value).Value >= 0);
}
/// <summary>
/// <see cref="PropertyMetadata.PropertyChangedCallback"/>
/// </summary>
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static void OnUserMinSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DefinitionBase definition = (DefinitionBase) d;
if (definition.InParentLogicalTree)
{
Grid parentGrid = (Grid) definition.Parent;
parentGrid.InvalidateMeasure();
}
}
/// <summary>
/// <see cref="DependencyProperty.ValidateValueCallback"/>
/// </summary>
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserMinSizePropertyValueValid(object value)
{
double v = (double)value;
return (!DoubleUtil.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
}
/// <summary>
/// <see cref="PropertyMetadata.PropertyChangedCallback"/>
/// </summary>
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static void OnUserMaxSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DefinitionBase definition = (DefinitionBase) d;
if (definition.InParentLogicalTree)
{
Grid parentGrid = (Grid) definition.Parent;
parentGrid.InvalidateMeasure();
}
}
/// <summary>
/// <see cref="DependencyProperty.ValidateValueCallback"/>
/// </summary>
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserMaxSizePropertyValueValid(object value)
{
double v = (double)value;
return (!DoubleUtil.IsNaN(v) && v >= 0.0d);
}
/// <summary>
/// <see cref="PropertyMetadata.PropertyChangedCallback"/>
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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
/// <summary>
/// Returns <c>true</c> if this definition is a part of shared group.
/// </summary>
internal bool IsShared
{
get { return (_sharedState != null); }
}
/// <summary>
/// Internal accessor to user size field.
/// </summary>
internal GridLength UserSize
{
get { return (_sharedState != null ? _sharedState.UserSize : UserSizeValueCache); }
}
/// <summary>
/// Internal accessor to user min size field.
/// </summary>
internal double UserMinSize
{
get { return (UserMinSizeValueCache); }
}
/// <summary>
/// Internal accessor to user max size field.
/// </summary>
internal double UserMaxSize
{
get { return (UserMaxSizeValueCache); }
}
/// <summary>
/// DefinitionBase's index in the parents collection.
/// </summary>
internal int Index
{
get
{
return (_parentIndex);
}
set
{
Debug.Assert(value >= -1 && _parentIndex != value);
_parentIndex = value;
}
}
/// <summary>
/// Layout-time user size type.
/// </summary>
internal Grid.LayoutTimeSizeType SizeType
{
get { return (_sizeType); }
set { _sizeType = value; }
}
/// <summary>
/// Returns or sets measure size for the definition.
/// </summary>
internal double MeasureSize
{
get { return (_measureSize); }
set { _measureSize = value; }
}
/// <summary>
/// Returns definition's layout time type sensitive preferred size.
/// </summary>
/// <remarks>
/// Returned value is guaranteed to be true preferred size.
/// </remarks>
internal double PreferredSize
{
get
{
double preferredSize = MinSize;
if ( _sizeType != Grid.LayoutTimeSizeType.Auto
&& preferredSize < _measureSize )
{
preferredSize = _measureSize;
}
return (preferredSize);
}
}
/// <summary>
/// Returns or sets size cache for the definition.
/// </summary>
internal double SizeCache
{
get { return (_sizeCache); }
set { _sizeCache = value; }
}
/// <summary>
/// Returns min size.
/// </summary>
internal double MinSize
{
get
{
double minSize = _minSize;
if ( UseSharedMinimum
&& _sharedState != null
&& minSize < _sharedState.MinSize )
{
minSize = _sharedState.MinSize;
}
return (minSize);
}
}
/// <summary>
/// Returns min size, always taking into account shared state.
/// </summary>
internal double MinSizeForArrange
{
get
{
double minSize = _minSize;
if ( _sharedState != null
&& (UseSharedMinimum || !LayoutWasUpdated)
&& minSize < _sharedState.MinSize )
{
minSize = _sharedState.MinSize;
}
return (minSize);
}
}
/// <summary>
/// Offset.
/// </summary>
internal double FinalOffset
{
get { return _offset; }
set { _offset = value; }
}
/// <summary>
/// Internal helper to access up-to-date UserSize property value.
/// </summary>
internal GridLength UserSizeValueCache
{
get
{
return (GridLength) GetValue(
_isColumnDefinition ?
ColumnDefinition.WidthProperty :
RowDefinition.HeightProperty);
}
}
/// <summary>
/// Internal helper to access up-to-date UserMinSize property value.
/// </summary>
internal double UserMinSizeValueCache
{
get
{
return (double) GetValue(
_isColumnDefinition ?
ColumnDefinition.MinWidthProperty :
RowDefinition.MinHeightProperty);
}
}
/// <summary>
/// Internal helper to access up-to-date UserMaxSize property value.
/// </summary>
internal double UserMaxSizeValueCache
{
get
{
return (double) GetValue(
_isColumnDefinition ?
ColumnDefinition.MaxWidthProperty :
RowDefinition.MaxHeightProperty);
}
}
/// <summary>
/// Protected. Returns <c>true</c> if this DefinitionBase instance is in parent's logical tree.
/// </summary>
internal bool InParentLogicalTree
{
get { return (_parentIndex != -1); }
}
#endregion Internal Properties
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
/// <summary>
/// SetFlags is used to set or unset one or multiple
/// flags on the object.
/// </summary>
private void SetFlags(bool value, Flags flags)
{
_flags = value ? (_flags | flags) : (_flags & (~flags));
}
/// <summary>
/// CheckFlagsAnd returns <c>true</c> if all the flags in the
/// given bitmask are set on the object.
/// </summary>
private bool CheckFlagsAnd(Flags flags)
{
return ((_flags & flags) == flags);
}
/// <summary>
/// <see cref="PropertyMetadata.PropertyChangedCallback"/>
/// </summary>
private static void OnSharedSizeGroupPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DefinitionBase definition = (DefinitionBase) d;
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);
}
}
}
}
/// <summary>
/// <see cref="DependencyProperty.ValidateValueCallback"/>
/// </summary>
/// <remarks>
/// Verifies that Shared Size Group Property string
/// a) not empty.
/// b) contains only letters, digits and underscore ('_').
/// c) does not start with a digit.
/// </remarks>
private static bool SharedSizeGroupPropertyValueValid(object 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);
}
/// <summary>
/// <see cref="PropertyMetadata.PropertyChangedCallback"/>
/// </summary>
/// <remark>
/// 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.
/// </remark>
private static void OnPrivateSharedSizeScopePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs 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
/// <summary>
/// Private getter of shared state collection dynamic property.
/// </summary>
private SharedSizeScope PrivateSharedSizeScope
{
get { return (SharedSizeScope) GetValue(PrivateSharedSizeScopeProperty); }
}
/// <summary>
/// Convenience accessor to UseSharedMinimum flag
/// </summary>
private bool UseSharedMinimum
{
get { return (CheckFlagsAnd(Flags.UseSharedMinimum)); }
set { SetFlags(value, Flags.UseSharedMinimum); }
}
/// <summary>
/// Convenience accessor to LayoutWasUpdated flag
/// </summary>
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
}
/// <summary>
/// Collection of shared states objects for a single scope
/// </summary>
private class SharedSizeScope
{
/// <summary>
/// Returns SharedSizeState object for a given group.
/// Creates a new StatedState object if necessary.
/// </summary>
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);
}
/// <summary>
/// Removes an entry in the registry by the given key.
/// </summary>
internal void Remove(object key)
{
Debug.Assert(_registry.Contains(key));
_registry.Remove(key);
}
private Hashtable _registry = new Hashtable(); // storage for shared state objects
}
/// <summary>
/// Implementation of per shared group state object
/// </summary>
private class SharedSizeState
{
/// <summary>
/// Default ctor.
/// </summary>
internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
{
Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
_sharedSizeScope = sharedSizeScope;
_sharedSizeGroupId = sharedSizeGroupId;
_registry = new List<DefinitionBase>();
_layoutUpdated = new EventHandler(OnLayoutUpdated);
_broadcastInvalidation = true;
}
/// <summary>
/// Adds / registers a definition instance.
/// </summary>
internal void AddMember(DefinitionBase member)
{
Debug.Assert(!_registry.Contains(member));
_registry.Add(member);
Invalidate();
}
/// <summary>
/// Removes / un-registers a definition instance.
/// </summary>
/// <remarks>
/// If the collection of registered definitions becomes empty
/// instantiates self removal from owner's collection.
/// </remarks>
internal void RemoveMember(DefinitionBase member)
{
Invalidate();
_registry.Remove(member);
if (_registry.Count == 0)
{
_sharedSizeScope.Remove(_sharedSizeGroupId);
}
}
/// <summary>
/// Propogates invalidations for all registered definitions.
/// Resets its own state.
/// </summary>
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;
}
}
/// <summary>
/// Makes sure that one and only one layout updated handler is registered for this shared state.
/// </summary>
internal void EnsureDeferredValidation(UIElement layoutUpdatedHost)
{
if (_layoutUpdatedHost == null)
{
_layoutUpdatedHost = layoutUpdatedHost;
_layoutUpdatedHost.LayoutUpdated += _layoutUpdated;
}
}
/// <summary>
/// DefinitionBase's specific code.
/// </summary>
internal double MinSize
{
get
{
if (!_userSizeValid) { EnsureUserSizeValid(); }
return (_minSize);
}
}
/// <summary>
/// DefinitionBase's specific code.
/// </summary>
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;
}
/// <summary>
/// OnLayoutUpdated handler. Validates that all participating definitions
/// have updated min size value. Forces another layout update cycle if needed.
/// </summary>
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<DefinitionBase> _registry; // registry of participating definitions
private readonly EventHandler _layoutUpdated; // instance event handler for layout updated event
private UIElement _layoutUpdatedHost; // UIElement 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
}
#endregion Private Structures Classes
//------------------------------------------------------
//
// Properties
//
//------------------------------------------------------
#region Properties
/// <summary>
/// Private shared size scope property holds a collection of shared state objects for the a given shared size scope.
/// <see cref="OnIsSharedSizeScopePropertyChanged"/>
/// </summary>
internal static readonly DependencyProperty PrivateSharedSizeScopeProperty =
DependencyProperty.RegisterAttached(
"PrivateSharedSizeScope",
typeof(SharedSizeScope),
typeof(DefinitionBase),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Shared size group property marks column / row definition as belonging to a group "Foo" or "Bar".
/// </summary>
/// <remarks>
/// Value of the Shared Size Group Property must satisfy the following rules:
/// <list type="bullet">
/// <item><description>
/// String must not be empty.
/// </description></item>
/// <item><description>
/// String must consist of letters, digits and underscore ('_') only.
/// </description></item>
/// <item><description>
/// String must not start with a digit.
/// </description></item>
/// </list>
/// </remarks>
public static readonly DependencyProperty SharedSizeGroupProperty =
DependencyProperty.Register(
"SharedSizeGroup",
typeof(string),
typeof(DefinitionBase),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnSharedSizeGroupPropertyChanged)),
new ValidateValueCallback(SharedSizeGroupPropertyValueValid));
/// <summary>
/// Static ctor. Used for static registration of properties.
/// </summary>
static DefinitionBase()
{
PrivateSharedSizeScopeProperty.OverrideMetadata(
typeof(DefinitionBase),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnPrivateSharedSizeScopePropertyChanged)));
}
#endregion Properties
}
}

4424
src/Avalonia.Controls/Grid.cs

File diff suppressed because it is too large

339
src/Avalonia.Controls/Grid/DefinitionBase.cs

@ -1,339 +0,0 @@
// 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;
using System.Diagnostics;
namespace Avalonia.Controls
{
/// <summary>
/// Base class for <see cref="ColumnDefinition"/> and <see cref="RowDefinition"/>.
/// </summary>
public abstract class DefinitionBase : AvaloniaObject
{
/// <summary>
/// Static ctor. Used for static registration of properties.
/// </summary>
static DefinitionBase()
{
SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase>(OnSharedSizeGroupPropertyChanged);
}
internal bool UseSharedMinimum { get; set; }
internal bool LayoutWasUpdated { get; set; }
private int _parentIndex = -1; // this instance's index in parent's children collection
private 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)
internal SharedSizeScope _privateSharedSizeScope;
private SharedSizeState _sharedState; // reference to shared state object this instance is registered with
/// <summary>
/// Defines the <see cref="SharedSizeGroup"/> property.
/// </summary>
public static readonly StyledProperty<string> SharedSizeGroupProperty =
AvaloniaProperty.Register<DefinitionBase, string>(nameof(SharedSizeGroup), inherits: true);
/// <summary>
/// Gets or sets the name of the shared size group of the column or row.
/// </summary>
public string SharedSizeGroup
{
get { return GetValue(SharedSizeGroupProperty); }
set { SetValue(SharedSizeGroupProperty, value); }
}
/// <summary>
/// Callback to notify about entering model tree.
/// </summary>
internal void OnEnterParentTree(Grid grid, int index)
{
Parent = grid;
_parentIndex = index;
}
internal void UpdateSharedScope()
{
if (_sharedState == null &
SharedSizeGroup != null &
Parent?.PrivateSharedSizeScope != null )
{
_privateSharedSizeScope = Parent.PrivateSharedSizeScope;
_sharedState = _privateSharedSizeScope.EnsureSharedState(SharedSizeGroup);
_sharedState.AddMember(this);
}
}
internal Grid Parent { get; set; }
/// <summary>
/// Callback to notify about exitting model tree.
/// </summary>
internal void OnExitParentTree()
{
_offset = 0;
if (_sharedState != null)
{
_sharedState.RemoveMember(this);
_sharedState = null;
}
}
/// <summary>
/// Performs action preparing definition to enter layout calculation mode.
/// </summary>
internal void OnBeforeLayout(Grid grid)
{
if (SharedSizeGroup != null)
UpdateSharedScope();
// reset layout state.
_minSize = 0;
LayoutWasUpdated = true;
// defer verification for shared definitions
if (_sharedState != null)
{
_sharedState.EnsureDeferredValidation(grid);
}
}
/// <summary>
/// Updates min size.
/// </summary>
/// <param name="minSize">New size.</param>
internal void UpdateMinSize(double minSize)
{
_minSize = Math.Max(_minSize, minSize);
}
/// <summary>
/// Sets min size.
/// </summary>
/// <param name="minSize">New size.</param>
internal void SetMinSize(double minSize)
{
_minSize = minSize;
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal void OnUserSizePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
_sharedState?.Invalidate();
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserMinSizePropertyValueValid(object value)
{
double v = (double)value;
return (!double.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static void OnUserMaxSizePropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e)
{
Grid parentGrid = (Grid)definition.Parent;
parentGrid.InvalidateMeasure();
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserMaxSizePropertyValueValid(object value)
{
double v = (double)value;
return (!double.IsNaN(v) && v >= 0.0d);
}
/// <summary>
/// Returns <c>true</c> if this definition is a part of shared group.
/// </summary>
internal bool IsShared
{
get { return (_sharedState != null); }
}
/// <summary>
/// Internal accessor to user size field.
/// </summary>
internal GridLength UserSize
{
get { return (_sharedState != null ? _sharedState.UserSize : UserSizeValueCache); }
}
/// <summary>
/// Internal accessor to user min size field.
/// </summary>
internal double UserMinSize
{
get { return (UserMinSizeValueCache); }
}
/// <summary>
/// Internal accessor to user max size field.
/// </summary>
internal double UserMaxSize
{
get { return (UserMaxSizeValueCache); }
}
/// <summary>
/// DefinitionBase's index in the parents collection.
/// </summary>
internal int Index
{
get
{
return (_parentIndex);
}
set
{
Debug.Assert(value >= -1 && _parentIndex != value);
_parentIndex = value;
}
}
/// <summary>
/// Layout-time user size type.
/// </summary>
internal LayoutTimeSizeType SizeType
{
get { return (_sizeType); }
set { _sizeType = value; }
}
/// <summary>
/// Returns or sets measure size for the definition.
/// </summary>
internal double MeasureSize
{
get { return (_measureSize); }
set { _measureSize = value; }
}
/// <summary>
/// Returns definition's layout time type sensitive preferred size.
/// </summary>
/// <remarks>
/// Returned value is guaranteed to be true preferred size.
/// </remarks>
internal double PreferredSize
{
get
{
double preferredSize = MinSize;
if (_sizeType != LayoutTimeSizeType.Auto
&& preferredSize < _measureSize)
{
preferredSize = _measureSize;
}
return (preferredSize);
}
}
/// <summary>
/// Returns or sets size cache for the definition.
/// </summary>
internal double SizeCache
{
get { return (_sizeCache); }
set { _sizeCache = value; }
}
/// <summary>
/// Returns min size.
/// </summary>
internal double MinSize
{
get
{
double minSize = _minSize;
if (UseSharedMinimum
&& _sharedState != null
&& minSize < _sharedState.MinSize)
{
minSize = _sharedState.MinSize;
}
return (minSize);
}
}
/// <summary>
/// Returns min size, always taking into account shared state.
/// </summary>
internal double MinSizeForArrange
{
get
{
double minSize = _minSize;
if (_sharedState != null
&& (UseSharedMinimum || !LayoutWasUpdated)
&& minSize < _sharedState.MinSize)
{
minSize = _sharedState.MinSize;
}
return (minSize);
}
}
/// <summary>
/// Offset.
/// </summary>
internal double FinalOffset
{
get { return _offset; }
set { _offset = value; }
}
/// <summary>
/// Internal helper to access up-to-date UserSize property value.
/// </summary>
internal abstract GridLength UserSizeValueCache { get; }
/// <summary>
/// Internal helper to access up-to-date UserMinSize property value.
/// </summary>
internal abstract double UserMinSizeValueCache { get; }
/// <summary>
/// Internal helper to access up-to-date UserMaxSize property value.
/// </summary>
internal abstract double UserMaxSizeValueCache { get; }
private static void OnSharedSizeGroupPropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e)
{
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))
{
var 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);
}
}
}
}
}

2378
src/Avalonia.Controls/Grid/Grid.cs

File diff suppressed because it is too large

26
src/Avalonia.Controls/Grid/GridCellCache.cs

@ -1,26 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Avalonia.Controls
{
/// <summary>
/// CellCache stored calculated values of
/// 1. attached cell positioning properties;
/// 2. size type;
/// 3. index of a next cell in the group;
/// </summary>
internal struct GridCellCache
{
internal int ColumnIndex;
internal int RowIndex;
internal int ColumnSpan;
internal int RowSpan;
internal LayoutTimeSizeType SizeTypeU;
internal LayoutTimeSizeType SizeTypeV;
internal int Next;
internal bool IsStarU { get { return ((SizeTypeU & LayoutTimeSizeType.Star) != 0); } }
internal bool IsAutoU { get { return ((SizeTypeU & LayoutTimeSizeType.Auto) != 0); } }
internal bool IsStarV { get { return ((SizeTypeV & LayoutTimeSizeType.Star) != 0); } }
internal bool IsAutoV { get { return ((SizeTypeV & LayoutTimeSizeType.Auto) != 0); } }
}
}

220
src/Avalonia.Controls/Grid/GridLength.cs

@ -1,220 +0,0 @@
// 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.Globalization;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
/// <summary>
/// Defines the valid units for a <see cref="GridLength"/>.
/// </summary>
public enum GridUnitType
{
/// <summary>
/// The row or column is auto-sized to fit its content.
/// </summary>
Auto = 0,
/// <summary>
/// The row or column is sized in device independent pixels.
/// </summary>
Pixel = 1,
/// <summary>
/// The row or column is sized as a weighted proportion of available space.
/// </summary>
Star = 2,
}
/// <summary>
/// Holds the width or height of a <see cref="Grid"/>'s column and row definitions.
/// </summary>
public struct GridLength : IEquatable<GridLength>
{
private readonly GridUnitType _type;
private readonly double _value;
/// <summary>
/// Initializes a new instance of the <see cref="GridLength"/> struct.
/// </summary>
/// <param name="value">The size of the GridLength in device independent pixels.</param>
public GridLength(double value)
: this(value, GridUnitType.Pixel)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GridLength"/> struct.
/// </summary>
/// <param name="value">The size of the GridLength.</param>
/// <param name="type">The unit of the GridLength.</param>
public GridLength(double value, GridUnitType type)
{
if (value < 0 || double.IsNaN(value) || double.IsInfinity(value))
{
throw new ArgumentException("Invalid value", nameof(value));
}
if (type < GridUnitType.Auto || type > GridUnitType.Star)
{
throw new ArgumentException("Invalid value", nameof(type));
}
_type = type;
_value = value;
}
/// <summary>
/// Gets an instance of <see cref="GridLength"/> that indicates that a row or column should
/// auto-size to fit its content.
/// </summary>
public static GridLength Auto => new GridLength(0, GridUnitType.Auto);
/// <summary>
/// Gets the unit of the <see cref="GridLength"/>.
/// </summary>
public GridUnitType GridUnitType => _type;
/// <summary>
/// Gets a value that indicates whether the <see cref="GridLength"/> has a <see cref="GridUnitType"/> of Pixel.
/// </summary>
public bool IsAbsolute => _type == GridUnitType.Pixel;
/// <summary>
/// Gets a value that indicates whether the <see cref="GridLength"/> has a <see cref="GridUnitType"/> of Auto.
/// </summary>
public bool IsAuto => _type == GridUnitType.Auto;
/// <summary>
/// Gets a value that indicates whether the <see cref="GridLength"/> has a <see cref="GridUnitType"/> of Star.
/// </summary>
public bool IsStar => _type == GridUnitType.Star;
/// <summary>
/// Gets the length.
/// </summary>
public double Value => _value;
/// <summary>
/// Compares two GridLength structures for equality.
/// </summary>
/// <param name="a">The first GridLength.</param>
/// <param name="b">The second GridLength.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public static bool operator ==(GridLength a, GridLength b)
{
return (a.IsAuto && b.IsAuto) || (a._value == b._value && a._type == b._type);
}
/// <summary>
/// Compares two GridLength structures for inequality.
/// </summary>
/// <param name="gl1">The first GridLength.</param>
/// <param name="gl2">The first GridLength.</param>
/// <returns>True if the structures are unequal, otherwise false.</returns>
public static bool operator !=(GridLength gl1, GridLength gl2)
{
return !(gl1 == gl2);
}
/// <summary>
/// Determines whether the <see cref="GridLength"/> is equal to the specified object.
/// </summary>
/// <param name="o">The object with which to test equality.</param>
/// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object o)
{
if (o == null)
{
return false;
}
if (!(o is GridLength))
{
return false;
}
return this == (GridLength)o;
}
/// <summary>
/// Compares two GridLength structures for equality.
/// </summary>
/// <param name="gridLength">The structure with which to test equality.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public bool Equals(GridLength gridLength)
{
return this == gridLength;
}
/// <summary>
/// Gets a hash code for the GridLength.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return _value.GetHashCode() ^ _type.GetHashCode();
}
/// <summary>
/// Gets a string representation of the <see cref="GridLength"/>.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
if (IsAuto)
{
return "Auto";
}
string s = _value.ToString();
return IsStar ? s + "*" : s;
}
/// <summary>
/// Parses a string to return a <see cref="GridLength"/>.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="GridLength"/>.</returns>
public static GridLength Parse(string s)
{
s = s.ToUpperInvariant();
if (s == "AUTO")
{
return Auto;
}
else if (s.EndsWith("*"))
{
var valueString = s.Substring(0, s.Length - 1).Trim();
var value = valueString.Length > 0 ? double.Parse(valueString, CultureInfo.InvariantCulture) : 1;
return new GridLength(value, GridUnitType.Star);
}
else
{
var value = double.Parse(s, CultureInfo.InvariantCulture);
return new GridLength(value, GridUnitType.Pixel);
}
}
/// <summary>
/// Parses a string to return a collection of <see cref="GridLength"/>s.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="GridLength"/>.</returns>
public static IEnumerable<GridLength> ParseLengths(string s)
{
using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture))
{
while (tokenizer.TryReadString(out var item))
{
yield return Parse(item);
}
}
}
}
}

91
src/Avalonia.Controls/Grid/GridLinesRenderer.cs

@ -1,91 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Avalonia.VisualTree;
using Avalonia.Media;
namespace Avalonia.Controls
{
internal class GridLinesRenderer : Control
{
/// <summary>
/// Static initialization
/// </summary>
static GridLinesRenderer()
{
var dashArray = new List<double>() { _dashLength, _dashLength };
var ds1 = new DashStyle(dashArray, 0);
_oddDashPen = new Pen(Brushes.Blue,
_penWidth,
lineCap: PenLineCap.Flat,
dashStyle: ds1);
var ds2 = new DashStyle(dashArray, _dashLength);
_evenDashPen = new Pen(Brushes.Yellow,
_penWidth,
lineCap: PenLineCap.Flat,
dashStyle: ds2);
}
/// <summary>
/// UpdateRenderBounds.
/// </summary>
public override void Render(DrawingContext drawingContext)
{
var grid = this.GetVisualParent<Grid>();
if (grid == null
|| !grid.ShowGridLines
|| grid.IsTrivialGrid)
{
return;
}
for (int i = 1; i < grid.ColumnDefinitions.Count; ++i)
{
DrawGridLine(
drawingContext,
grid.ColumnDefinitions[i].FinalOffset, 0.0,
grid.ColumnDefinitions[i].FinalOffset, _lastArrangeSize.Height);
}
for (int i = 1; i < grid.RowDefinitions.Count; ++i)
{
DrawGridLine(
drawingContext,
0.0, grid.RowDefinitions[i].FinalOffset,
_lastArrangeSize.Width, grid.RowDefinitions[i].FinalOffset);
}
}
/// <summary>
/// Draw single hi-contrast line.
/// </summary>
private static void DrawGridLine(
DrawingContext drawingContext,
double startX,
double startY,
double endX,
double endY)
{
var start = new Point(startX, startY);
var end = new Point(endX, endY);
drawingContext.DrawLine(_oddDashPen, start, end);
drawingContext.DrawLine(_evenDashPen, start, end);
}
internal void UpdateRenderBounds(Size arrangeSize)
{
_lastArrangeSize = arrangeSize;
this.InvalidateVisual();
}
private static Size _lastArrangeSize;
private const double _dashLength = 4.0; //
private const double _penWidth = 1.0; //
private static readonly Pen _oddDashPen; // first pen to draw dash
private static readonly Pen _evenDashPen; // second pen to draw dash
}
}

69
src/Avalonia.Controls/Grid/GridSpanKey.cs

@ -1,69 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Avalonia.Controls
{
/// <summary>
/// Helper class for representing a key for a span in hashtable.
/// </summary>
internal class GridSpanKey
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="start">Starting index of the span.</param>
/// <param name="count">Span count.</param>
/// <param name="u"><c>true</c> for columns; <c>false</c> for rows.</param>
internal GridSpanKey(int start, int count, bool u)
{
_start = start;
_count = count;
_u = u;
}
/// <summary>
/// <see cref="object.GetHashCode"/>
/// </summary>
public override int GetHashCode()
{
int hash = (_start ^ (_count << 2));
if (_u) hash &= 0x7ffffff;
else hash |= 0x8000000;
return (hash);
}
/// <summary>
/// <see cref="object.Equals(object)"/>
/// </summary>
public override bool Equals(object obj)
{
GridSpanKey sk = obj as GridSpanKey;
return (sk != null
&& sk._start == _start
&& sk._count == _count
&& sk._u == _u);
}
/// <summary>
/// Returns start index of the span.
/// </summary>
internal int Start { get { return (_start); } }
/// <summary>
/// Returns span count.
/// </summary>
internal int Count { get { return (_count); } }
/// <summary>
/// Returns <c>true</c> if this is a column span.
/// <c>false</c> if this is a row span.
/// </summary>
internal bool U { get { return (_u); } }
private int _start;
private int _count;
private bool _u;
}
}

209
src/Avalonia.Controls/Grid/GridSplitter.cs

@ -1,209 +0,0 @@
// 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.Linq;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Represents the control that redistributes space between columns or rows of a Grid control.
/// </summary>
/// <remarks>
/// Unlike WPF GridSplitter, Avalonia GridSplitter has only one Behavior, GridResizeBehavior.PreviousAndNext.
/// </remarks>
public class GridSplitter : Thumb
{
private List<DefinitionBase> _definitions;
private Grid _grid;
private DefinitionBase _nextDefinition;
private Orientation _orientation;
private DefinitionBase _prevDefinition;
private void GetDeltaConstraints(out double min, out double max)
{
var prevDefinitionLen = GetActualLength(_prevDefinition);
var prevDefinitionMin = GetMinLength(_prevDefinition);
var prevDefinitionMax = GetMaxLength(_prevDefinition);
var nextDefinitionLen = GetActualLength(_nextDefinition);
var nextDefinitionMin = GetMinLength(_nextDefinition);
var nextDefinitionMax = GetMaxLength(_nextDefinition);
// Determine the minimum and maximum the columns can be resized
min = -Math.Min(prevDefinitionLen - prevDefinitionMin, nextDefinitionMax - nextDefinitionLen);
max = Math.Min(prevDefinitionMax - prevDefinitionLen, nextDefinitionLen - nextDefinitionMin);
}
protected override void OnDragDelta(VectorEventArgs e)
{
// WPF doesn't change anything when spliter is in the last row/column
// but resizes the splitter row/column when it's the first one.
// this is different, but more internally consistent.
if (_prevDefinition == null || _nextDefinition == null)
return;
var delta = _orientation == Orientation.Vertical ? e.Vector.X : e.Vector.Y;
double max;
double min;
GetDeltaConstraints(out min, out max);
delta = Math.Min(Math.Max(delta, min), max);
var prevIsStar = IsStar(_prevDefinition);
var nextIsStar = IsStar(_nextDefinition);
if (prevIsStar && nextIsStar)
{
foreach (var definition in _definitions)
{
if (definition == _prevDefinition)
{
SetLengthInStars(_prevDefinition, GetActualLength(_prevDefinition) + delta);
}
else if (definition == _nextDefinition)
{
SetLengthInStars(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
else if (IsStar(definition))
{
SetLengthInStars(definition, GetActualLength(definition)); // same size but in stars.
}
}
}
else if (prevIsStar)
{
SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
else if (nextIsStar)
{
SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta);
}
else
{
SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta);
SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
}
private double GetActualLength(DefinitionBase definition)
{
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.ActualWidth ?? ((RowDefinition)definition).ActualHeight;
}
private double GetMinLength(DefinitionBase definition)
{
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.MinWidth ?? ((RowDefinition)definition).MinHeight;
}
private double GetMaxLength(DefinitionBase definition)
{
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.MaxWidth ?? ((RowDefinition)definition).MaxHeight;
}
private bool IsStar(DefinitionBase definition)
{
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.Width.IsStar ?? ((RowDefinition)definition).Height.IsStar;
}
private void SetLengthInStars(DefinitionBase definition, double value)
{
var columnDefinition = definition as ColumnDefinition;
if (columnDefinition != null)
{
columnDefinition.Width = new GridLength(value, GridUnitType.Star);
}
else
{
((RowDefinition)definition).Height = new GridLength(value, GridUnitType.Star);
}
}
private void SetLength(DefinitionBase definition, double value)
{
var columnDefinition = definition as ColumnDefinition;
if (columnDefinition != null)
{
columnDefinition.Width = new GridLength(value);
}
else
{
((RowDefinition)definition).Height = new GridLength(value);
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_grid = this.GetVisualParent<Grid>();
_orientation = DetectOrientation();
int definitionIndex; //row or col
if (_orientation == Orientation.Vertical)
{
Cursor = new Cursor(StandardCursorType.SizeWestEast);
_definitions = _grid.ColumnDefinitions.Cast<DefinitionBase>().ToList();
definitionIndex = GetValue(Grid.ColumnProperty);
PseudoClasses.Add(":vertical");
}
else
{
Cursor = new Cursor(StandardCursorType.SizeNorthSouth);
definitionIndex = GetValue(Grid.RowProperty);
_definitions = _grid.RowDefinitions.Cast<DefinitionBase>().ToList();
PseudoClasses.Add(":horizontal");
}
if (definitionIndex > 0)
_prevDefinition = _definitions[definitionIndex - 1];
if (definitionIndex < _definitions.Count - 1)
_nextDefinition = _definitions[definitionIndex + 1];
}
private Orientation DetectOrientation()
{
if (!_grid.ColumnDefinitions.Any())
return Orientation.Horizontal;
if (!_grid.RowDefinitions.Any())
return Orientation.Vertical;
var col = GetValue(Grid.ColumnProperty);
var row = GetValue(Grid.RowProperty);
var width = _grid.ColumnDefinitions[col].Width;
var height = _grid.RowDefinitions[row].Height;
if (width.IsAuto && !height.IsAuto)
{
return Orientation.Vertical;
}
if (!width.IsAuto && height.IsAuto)
{
return Orientation.Horizontal;
}
if (_grid.Children.OfType<Control>() // Decision based on other controls in the same column
.Where(c => Grid.GetColumn(c) == col)
.Any(c => c.GetType() != typeof(GridSplitter)))
{
return Orientation.Horizontal;
}
return Orientation.Vertical;
}
}
}

17
src/Avalonia.Controls/Grid/LayoutTimeSizeType.cs

@ -1,17 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Avalonia.Controls
{
/// <summary>
/// LayoutTimeSizeType is used internally and reflects layout-time size type.
/// </summary>
[System.Flags]
internal enum LayoutTimeSizeType : byte
{
None = 0x00,
Pixel = 0x01,
Auto = 0x02,
Star = 0x04,
}
}

31
src/Avalonia.Controls/Grid/MaxRatioComparer.cs

@ -1,31 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// MaxRatioComparer.
/// Sort by w/max (stored in SizeCache), ascending.
/// We query the list from the back, i.e. in descending order of w/max.
/// </summary>
internal class MaxRatioComparer : IComparer
{
public int Compare(object x, object y)
{
DefinitionBase definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase;
int result;
if (!Grid.CompareNullRefs(definitionX, definitionY, out result))
{
result = definitionX.SizeCache.CompareTo(definitionY.SizeCache);
}
return result;
}
}
}

46
src/Avalonia.Controls/Grid/MaxRatioIndexComparer.cs

@ -1,46 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
namespace Avalonia.Controls
{
internal class MaxRatioIndexComparer : IComparer
{
private readonly DefinitionBase[] definitions;
internal MaxRatioIndexComparer(DefinitionBase[] definitions)
{
Contract.Requires<NullReferenceException>(definitions != null);
this.definitions = definitions;
}
public int Compare(object x, object y)
{
int? indexX = x as int?;
int? indexY = y as int?;
DefinitionBase definitionX = null;
DefinitionBase definitionY = null;
if (indexX != null)
{
definitionX = definitions[indexX.Value];
}
if (indexY != null)
{
definitionY = definitions[indexY.Value];
}
int result;
if (!Grid.CompareNullRefs(definitionX, definitionY, out result))
{
result = definitionX.SizeCache.CompareTo(definitionY.SizeCache);
}
return result;
}
}
}

30
src/Avalonia.Controls/Grid/MinRatioComparer.cs

@ -1,30 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// MinRatioComparer.
/// Sort by w/min (stored in MeasureSize), descending.
/// We query the list from the back, i.e. in ascending order of w/min.
/// </summary>
internal class MinRatioComparer : IComparer
{
public int Compare(object x, object y)
{
DefinitionBase definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase;
int result;
if (!Grid.CompareNullRefs(definitionY, definitionX, out result))
{
result = definitionY.MeasureSize.CompareTo(definitionX.MeasureSize);
}
return result;
}
}
}

46
src/Avalonia.Controls/Grid/MinRatioIndexComparer.cs

@ -1,46 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
namespace Avalonia.Controls
{
internal class MinRatioIndexComparer : IComparer
{
private readonly DefinitionBase[] definitions;
internal MinRatioIndexComparer(DefinitionBase[] definitions)
{
Contract.Requires<NullReferenceException>(definitions != null);
this.definitions = definitions;
}
public int Compare(object x, object y)
{
int? indexX = x as int?;
int? indexY = y as int?;
DefinitionBase definitionX = null;
DefinitionBase definitionY = null;
if (indexX != null)
{
definitionX = definitions[indexX.Value];
}
if (indexY != null)
{
definitionY = definitions[indexY.Value];
}
int result;
if (!Grid.CompareNullRefs(definitionY, definitionX, out result))
{
result = definitionY.MeasureSize.CompareTo(definitionX.MeasureSize);
}
return result;
}
}
}

36
src/Avalonia.Controls/Grid/RoundingErrorIndexComparer.cs

@ -1,36 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
namespace Avalonia.Controls
{
internal class RoundingErrorIndexComparer : IComparer
{
private readonly double[] errors;
internal RoundingErrorIndexComparer(double[] errors)
{
Contract.Requires<NullReferenceException>(errors != null);
this.errors = errors;
}
public int Compare(object x, object y)
{
int? indexX = x as int?;
int? indexY = y as int?;
int result;
if (!Grid.CompareNullRefs(indexX, indexY, out result))
{
double errorX = errors[indexX.Value];
double errorY = errors[indexY.Value];
result = errorX.CompareTo(errorY);
}
return result;
}
}
}

43
src/Avalonia.Controls/Grid/SharedSizeScope.cs

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Diagnostics;
namespace Avalonia.Controls
{
/// <summary>
/// Collection of shared states objects for a single scope
/// </summary>
internal class SharedSizeScope
{
/// <summary>
/// Returns SharedSizeState object for a given group.
/// Creates a new StatedState object if necessary.
/// </summary>
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);
}
/// <summary>
/// Removes an entry in the registry by the given key.
/// </summary>
internal void Remove(object key)
{
Debug.Assert(_registry.Contains(key));
_registry.Remove(key);
}
private Hashtable _registry = new Hashtable(); // storage for shared state objects
}
}

209
src/Avalonia.Controls/Grid/SharedSizeState.cs

@ -1,209 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
/// <summary>
/// Implementation of per shared group state object
/// </summary>
internal class SharedSizeState
{
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<DefinitionBase> _registry; // registry of participating definitions
private readonly EventHandler _layoutUpdated; // instance event handler for layout updated event
private Control _layoutUpdatedHost; // Control 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
/// <summary>
/// Default ctor.
/// </summary>
internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
{
Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
_sharedSizeScope = sharedSizeScope;
_sharedSizeGroupId = sharedSizeGroupId;
_registry = new List<DefinitionBase>();
_layoutUpdated = new EventHandler(OnLayoutUpdated);
_broadcastInvalidation = true;
}
/// <summary>
/// Adds / registers a definition instance.
/// </summary>
internal void AddMember(DefinitionBase member)
{
Debug.Assert(!_registry.Contains(member));
_registry.Add(member);
Invalidate();
}
/// <summary>
/// Removes / un-registers a definition instance.
/// </summary>
/// <remarks>
/// If the collection of registered definitions becomes empty
/// instantiates self removal from owner's collection.
/// </remarks>
internal void RemoveMember(DefinitionBase member)
{
Invalidate();
_registry.Remove(member);
if (_registry.Count == 0)
{
_sharedSizeScope.Remove(_sharedSizeGroupId);
}
}
/// <summary>
/// Propogates invalidations for all registered definitions.
/// Resets its own state.
/// </summary>
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;
}
}
/// <summary>
/// Makes sure that one and only one layout updated handler is registered for this shared state.
/// </summary>
internal void EnsureDeferredValidation(Control layoutUpdatedHost)
{
if (_layoutUpdatedHost == null)
{
_layoutUpdatedHost = layoutUpdatedHost;
_layoutUpdatedHost.LayoutUpdated += _layoutUpdated;
}
}
/// <summary>
/// DefinitionBase's specific code.
/// </summary>
internal double MinSize
{
get
{
if (!_userSizeValid) { EnsureUserSizeValid(); }
return (_minSize);
}
}
/// <summary>
/// DefinitionBase's specific code.
/// </summary>
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;
}
/// <summary>
/// OnLayoutUpdated handler. Validates that all participating definitions
/// have updated min size value. Forces another layout update cycle if needed.
/// </summary>
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 = !MathUtilities.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 (!MathUtilities.AreClose(sharedMinSize, definitionBase.MinSize))
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateMeasure();
definitionBase.UseSharedMinimum = true;
}
else
{
definitionBase.UseSharedMinimum = false;
// if measure is valid then also need to check arrange.
// Note: definitionBase.SizeCache is volatile but at this point
// it contains up-to-date final size
if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateArrange();
}
}
definitionBase.LayoutWasUpdated = false;
}
}
_minSize = sharedMinSize;
_layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
_layoutUpdatedHost = null;
_broadcastInvalidation = true;
}
}
}

46
src/Avalonia.Controls/Grid/SpanMaxDistributionOrderComparer.cs

@ -1,46 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
namespace Avalonia.Controls
{
internal class SpanMaxDistributionOrderComparer : IComparer
{
public int Compare(object x, object y)
{
DefinitionBase definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase;
int result;
if (!Grid.CompareNullRefs(definitionX, definitionY, out result))
{
if (definitionX.UserSize.IsAuto)
{
if (definitionY.UserSize.IsAuto)
{
result = definitionX.SizeCache.CompareTo(definitionY.SizeCache);
}
else
{
result = +1;
}
}
else
{
if (definitionY.UserSize.IsAuto)
{
result = -1;
}
else
{
result = definitionX.SizeCache.CompareTo(definitionY.SizeCache);
}
}
}
return result;
}
}
}

46
src/Avalonia.Controls/Grid/SpanPreferredDistributionOrderComparer.cs

@ -1,46 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
namespace Avalonia.Controls
{
internal class SpanPreferredDistributionOrderComparer : IComparer
{
public int Compare(object x, object y)
{
DefinitionBase definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase;
int result;
if (!Grid.CompareNullRefs(definitionX, definitionY, out result))
{
if (definitionX.UserSize.IsAuto)
{
if (definitionY.UserSize.IsAuto)
{
result = definitionX.MinSize.CompareTo(definitionY.MinSize);
}
else
{
result = -1;
}
}
else
{
if (definitionY.UserSize.IsAuto)
{
result = +1;
}
else
{
result = definitionX.PreferredSize.CompareTo(definitionY.PreferredSize);
}
}
}
return result;
}
}
}

29
src/Avalonia.Controls/Grid/StarWeightComparer.cs

@ -1,29 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// StarWeightComparer.
/// Sort by *-weight (stored in MeasureSize), ascending.
/// </summary>
internal class StarWeightComparer : IComparer
{
public int Compare(object x, object y)
{
DefinitionBase definitionX = x as DefinitionBase;
DefinitionBase definitionY = y as DefinitionBase;
int result;
if (!Grid.CompareNullRefs(definitionX, definitionY, out result))
{
result = definitionX.MeasureSize.CompareTo(definitionY.MeasureSize);
}
return result;
}
}
}

47
src/Avalonia.Controls/Grid/StarWeightIndexComparer.cs

@ -1,47 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
namespace Avalonia.Controls
{
internal class StarWeightIndexComparer : IComparer
{
private readonly DefinitionBase[] definitions;
internal StarWeightIndexComparer(DefinitionBase[] definitions)
{
Contract.Requires<NullReferenceException>(definitions != null);
this.definitions = definitions;
}
public int Compare(object x, object y)
{
int? indexX = x as int?;
int? indexY = y as int?;
DefinitionBase definitionX = null;
DefinitionBase definitionY = null;
if (indexX != null)
{
definitionX = definitions[indexX.Value];
}
if (indexY != null)
{
definitionY = definitions[indexY.Value];
}
int result;
if (!Grid.CompareNullRefs(definitionX, definitionY, out result))
{
result = definitionX.MeasureSize.CompareTo(definitionY.MeasureSize);
}
return result;
}
}
}

7
src/Avalonia.Controls/Grid/RowDefinition.cs → src/Avalonia.Controls/RowDefinition.cs

@ -87,12 +87,5 @@ namespace Avalonia.Controls
get { return GetValue(HeightProperty); }
set { SetValue(HeightProperty, value); }
}
internal override GridLength UserSizeValueCache => this.Height;
internal override double UserMinSizeValueCache => this.MinHeight;
internal override double UserMaxSizeValueCache => this.MaxHeight;
}
}

0
src/Avalonia.Controls/Grid/RowDefinitions.cs → src/Avalonia.Controls/RowDefinitions.cs

Loading…
Cancel
Save