Browse Source

Implemented coercion.

refactor/style-priorities
Steven Kirk 4 years ago
parent
commit
fb3e9d71b7
  1. 6
      src/Avalonia.Base/AvaloniaObject.cs
  2. 7
      src/Avalonia.Base/PropertyStore/EffectiveValue.cs
  3. 84
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  4. 8
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  5. 2
      src/Avalonia.Base/StyledPropertyBase.cs
  6. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs

6
src/Avalonia.Base/AvaloniaObject.cs

@ -462,11 +462,7 @@ namespace Avalonia
/// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="property">The property.</param>
public void CoerceValue(AvaloniaProperty property)
{
throw new NotImplementedException();
////_values?.CoerceValue(property);
}
public void CoerceValue(AvaloniaProperty property) => _values.CoerceValue(property);
/// <inheritdoc/>
internal void AddInheritanceChild(AvaloniaObject child)

7
src/Avalonia.Base/PropertyStore/EffectiveValue.cs

@ -103,6 +103,13 @@ namespace Avalonia.PropertyStore
EffectiveValue? oldValue,
EffectiveValue? newValue);
/// <summary>
/// Coerces the property value.
/// </summary>
/// <param name="owner">The associated value store.</param>
/// <param name="property">The property to coerce.</param>
public abstract void CoerceValue(ValueStore owner, AvaloniaProperty property);
/// <summary>
/// Disposes the effective value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary.

84
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Data;
@ -15,12 +16,31 @@ namespace Avalonia.PropertyStore
internal sealed class EffectiveValue<T> : EffectiveValue
{
private T? _baseValue;
private UncommonFields? _uncommon;
public EffectiveValue(T value, BindingPriority priority)
public EffectiveValue(
AvaloniaObject owner,
StyledPropertyBase<T> property,
T value,
BindingPriority priority)
{
Value = value;
Priority = priority;
if (property.HasCoercion &&
property.GetMetadata(owner.GetType()) is { } metadata &&
metadata.CoerceValue is { } coerce)
{
_uncommon = new()
{
_coerce = coerce,
_uncoercedValue = value,
_uncoercedBaseValue = value,
};
value = coerce(owner, value);
}
if (priority >= BindingPriority.LocalValue && priority < BindingPriority.Inherited)
{
_baseValue = value;
@ -88,19 +108,27 @@ namespace Avalonia.PropertyStore
var oldValue = Value;
var valueChanged = false;
var baseValueChanged = false;
var v = value;
if (_uncommon?._coerce is { } coerce)
v = coerce(owner.Owner, value);
if (priority <= Priority)
{
valueChanged = !EqualityComparer<T>.Default.Equals(Value, value);
Value = value;
valueChanged = !EqualityComparer<T>.Default.Equals(Value, v);
Value = v;
Priority = priority;
if (_uncommon is not null)
_uncommon._uncoercedValue = value;
}
if (priority <= BasePriority && priority >= BindingPriority.LocalValue)
{
baseValueChanged = !EqualityComparer<T>.Default.Equals(_baseValue, value);
_baseValue = value;
baseValueChanged = !EqualityComparer<T>.Default.Equals(_baseValue, v);
_baseValue = v;
BasePriority = priority;
if (_uncommon is not null)
_uncommon._uncoercedBaseValue = value;
}
if (valueChanged)
@ -135,22 +163,36 @@ namespace Avalonia.PropertyStore
{
Debug.Assert(priority < BindingPriority.Inherited);
Debug.Assert(basePriority > BindingPriority.Animation);
Debug.Assert(priority <= basePriority);
var oldValue = Value;
var valueChanged = false;
var baseValueChanged = false;
var v = value;
var bv = baseValue;
if (!EqualityComparer<T>.Default.Equals(Value, value))
if (_uncommon?._coerce is { } coerce)
{
Value = value;
v = coerce(owner.Owner, value);
bv = coerce(owner.Owner, baseValue);
}
if (priority != BindingPriority.Unset && !EqualityComparer<T>.Default.Equals(Value, v))
{
Value = v;
valueChanged = true;
if (_uncommon is not null)
_uncommon._uncoercedValue = value;
}
if (BasePriority == BindingPriority.Unset ||
!EqualityComparer<T>.Default.Equals(_baseValue, baseValue))
if (priority != BindingPriority.Unset &&
(BasePriority == BindingPriority.Unset ||
!EqualityComparer<T>.Default.Equals(_baseValue, bv)))
{
_baseValue = value;
_baseValue = v;
baseValueChanged = true;
if (_uncommon is not null)
_uncommon._uncoercedValue = baseValue;
}
Priority = priority;
@ -193,6 +235,19 @@ namespace Avalonia.PropertyStore
}
}
public override void CoerceValue(ValueStore owner, AvaloniaProperty property)
{
if (_uncommon is null)
return;
SetAndRaise(
owner,
(StyledPropertyBase<T>)property,
_uncommon._uncoercedValue!,
Priority,
_uncommon._uncoercedBaseValue!,
BasePriority);
}
public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property)
{
DisposeAndRaiseUnset(owner, (StyledPropertyBase<T>)property);
@ -216,5 +271,12 @@ namespace Avalonia.PropertyStore
{
return BasePriority != BindingPriority.Unset ? _baseValue : AvaloniaProperty.UnsetValue;
}
private class UncommonFields
{
public Func<IAvaloniaObject, T, T>? _coerce;
public T? _uncoercedValue;
public T? _uncoercedBaseValue;
}
}
}

8
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -189,6 +189,12 @@ namespace Avalonia.PropertyStore
return false;
}
public void CoerceValue(AvaloniaProperty property)
{
if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
v.CoerceValue(this, property);
}
public Optional<T> GetBaseValue<T>(StyledPropertyBase<T> property)
{
if (TryGetEffectiveValue(property, out var v) &&
@ -628,7 +634,7 @@ namespace Avalonia.PropertyStore
{
Debug.Assert(priority < BindingPriority.Inherited);
var defaultValue = property.GetDefaultValue(Owner.GetType());
var effectiveValue = new EffectiveValue<T>(defaultValue, BindingPriority.Unset);
var effectiveValue = new EffectiveValue<T>(Owner, property, defaultValue, BindingPriority.Unset);
AddEffectiveValue(property, effectiveValue);
effectiveValue.SetAndRaise(this, property, value, priority);
}

2
src/Avalonia.Base/StyledPropertyBase.cs

@ -183,7 +183,7 @@ namespace Avalonia
internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o)
{
return new EffectiveValue<TValue>(GetDefaultValue(o.GetType()), BindingPriority.Unset);
return new EffectiveValue<TValue>(o, this, GetDefaultValue(o.GetType()), BindingPriority.Unset);
}
/// <inheritdoc/>

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs

@ -73,7 +73,7 @@ namespace Avalonia.Base.UnitTests
var source1 = new Subject<BindingValue<int>>();
var source2 = new Subject<BindingValue<int>>();
target.Bind(Class1.FooProperty, source1);
target.Bind(Class1.FooProperty, source1, BindingPriority.Style);
source1.OnNext(150);
target.Bind(Class1.FooProperty, source2);

Loading…
Cancel
Save