25 changed files with 5416 additions and 3971 deletions
@ -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
|
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -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); |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -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); } } |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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, |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue