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"/>. /// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
public void CoerceValue(AvaloniaProperty property) public void CoerceValue(AvaloniaProperty property) => _values.CoerceValue(property);
{
throw new NotImplementedException();
////_values?.CoerceValue(property);
}
/// <inheritdoc/> /// <inheritdoc/>
internal void AddInheritanceChild(AvaloniaObject child) internal void AddInheritanceChild(AvaloniaObject child)

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

@ -103,6 +103,13 @@ namespace Avalonia.PropertyStore
EffectiveValue? oldValue, EffectiveValue? oldValue,
EffectiveValue? newValue); 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> /// <summary>
/// Disposes the effective value, raising <see cref="AvaloniaObject.PropertyChanged"/> /// Disposes the effective value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary. /// 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;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Avalonia.Data; using Avalonia.Data;
@ -15,12 +16,31 @@ namespace Avalonia.PropertyStore
internal sealed class EffectiveValue<T> : EffectiveValue internal sealed class EffectiveValue<T> : EffectiveValue
{ {
private T? _baseValue; 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; Value = value;
Priority = priority; 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) if (priority >= BindingPriority.LocalValue && priority < BindingPriority.Inherited)
{ {
_baseValue = value; _baseValue = value;
@ -88,19 +108,27 @@ namespace Avalonia.PropertyStore
var oldValue = Value; var oldValue = Value;
var valueChanged = false; var valueChanged = false;
var baseValueChanged = false; var baseValueChanged = false;
var v = value;
if (_uncommon?._coerce is { } coerce)
v = coerce(owner.Owner, value);
if (priority <= Priority) if (priority <= Priority)
{ {
valueChanged = !EqualityComparer<T>.Default.Equals(Value, value); valueChanged = !EqualityComparer<T>.Default.Equals(Value, v);
Value = value; Value = v;
Priority = priority; Priority = priority;
if (_uncommon is not null)
_uncommon._uncoercedValue = value;
} }
if (priority <= BasePriority && priority >= BindingPriority.LocalValue) if (priority <= BasePriority && priority >= BindingPriority.LocalValue)
{ {
baseValueChanged = !EqualityComparer<T>.Default.Equals(_baseValue, value); baseValueChanged = !EqualityComparer<T>.Default.Equals(_baseValue, v);
_baseValue = value; _baseValue = v;
BasePriority = priority; BasePriority = priority;
if (_uncommon is not null)
_uncommon._uncoercedBaseValue = value;
} }
if (valueChanged) if (valueChanged)
@ -135,22 +163,36 @@ namespace Avalonia.PropertyStore
{ {
Debug.Assert(priority < BindingPriority.Inherited); Debug.Assert(priority < BindingPriority.Inherited);
Debug.Assert(basePriority > BindingPriority.Animation); Debug.Assert(basePriority > BindingPriority.Animation);
Debug.Assert(priority <= basePriority);
var oldValue = Value; var oldValue = Value;
var valueChanged = false; var valueChanged = false;
var baseValueChanged = 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; valueChanged = true;
if (_uncommon is not null)
_uncommon._uncoercedValue = value;
} }
if (BasePriority == BindingPriority.Unset || if (priority != BindingPriority.Unset &&
!EqualityComparer<T>.Default.Equals(_baseValue, baseValue)) (BasePriority == BindingPriority.Unset ||
!EqualityComparer<T>.Default.Equals(_baseValue, bv)))
{ {
_baseValue = value; _baseValue = v;
baseValueChanged = true; baseValueChanged = true;
if (_uncommon is not null)
_uncommon._uncoercedValue = baseValue;
} }
Priority = priority; 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) public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property)
{ {
DisposeAndRaiseUnset(owner, (StyledPropertyBase<T>)property); DisposeAndRaiseUnset(owner, (StyledPropertyBase<T>)property);
@ -216,5 +271,12 @@ namespace Avalonia.PropertyStore
{ {
return BasePriority != BindingPriority.Unset ? _baseValue : AvaloniaProperty.UnsetValue; 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; 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) public Optional<T> GetBaseValue<T>(StyledPropertyBase<T> property)
{ {
if (TryGetEffectiveValue(property, out var v) && if (TryGetEffectiveValue(property, out var v) &&
@ -628,7 +634,7 @@ namespace Avalonia.PropertyStore
{ {
Debug.Assert(priority < BindingPriority.Inherited); Debug.Assert(priority < BindingPriority.Inherited);
var defaultValue = property.GetDefaultValue(Owner.GetType()); 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); AddEffectiveValue(property, effectiveValue);
effectiveValue.SetAndRaise(this, property, value, priority); effectiveValue.SetAndRaise(this, property, value, priority);
} }

2
src/Avalonia.Base/StyledPropertyBase.cs

@ -183,7 +183,7 @@ namespace Avalonia
internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) 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/> /// <inheritdoc/>

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

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

Loading…
Cancel
Save