Skip to content

Commit

Permalink
Merge pull request #3636 from AvaloniaUI/refactor/styling
Browse files Browse the repository at this point in the history
Refactor styling
  • Loading branch information
grokys authored Mar 5, 2020
2 parents 458a0c6 + 146fd8f commit a70f774
Show file tree
Hide file tree
Showing 73 changed files with 1,819 additions and 1,527 deletions.
9 changes: 7 additions & 2 deletions src/Avalonia.Base/AvaloniaObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,10 @@ public void SetValue(
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
public void SetValue<T>(
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
public IDisposable SetValue<T>(
StyledPropertyBase<T> property,
T value,
BindingPriority priority = BindingPriority.LocalValue)
Expand All @@ -335,8 +338,10 @@ public void SetValue<T>(
}
else if (!(value is DoNothingType))
{
Values.SetValue(property, value, priority);
return Values.SetValue(property, value, priority);
}

return null;
}

/// <summary>
Expand Down
17 changes: 11 additions & 6 deletions src/Avalonia.Base/AvaloniaObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,10 @@ public static T GetValue<T>(this IAvaloniaObject target, AvaloniaProperty<T> pro
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
public static void SetValue(
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
public static IDisposable SetValue(
this IAvaloniaObject target,
AvaloniaProperty property,
object value,
Expand All @@ -467,7 +470,7 @@ public static void SetValue(
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));

property.RouteSetValue(target, value, priority);
return property.RouteSetValue(target, value, priority);
}

/// <summary>
Expand All @@ -478,7 +481,10 @@ public static void SetValue(
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
public static void SetValue<T>(
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
public static IDisposable SetValue<T>(
this IAvaloniaObject target,
AvaloniaProperty<T> property,
T value,
Expand All @@ -490,11 +496,10 @@ public static void SetValue<T>(
switch (property)
{
case StyledPropertyBase<T> styled:
target.SetValue(styled, value, priority);
break;
return target.SetValue(styled, value, priority);
case DirectPropertyBase<T> direct:
target.SetValue(direct, value);
break;
return null;
default:
throw new NotSupportedException("Unsupported AvaloniaProperty type.");
}
Expand Down
14 changes: 13 additions & 1 deletion src/Avalonia.Base/AvaloniaProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,15 @@ public override string ToString()
return Name;
}

/// <summary>
/// Uses the visitor pattern to resolve an untyped property to a typed property.
/// </summary>
/// <typeparam name="TData">The type of user data passed.</typeparam>
/// <param name="vistor">The visitor which will accept the typed property.</param>
/// <param name="data">The user data to pass.</param>
public abstract void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
where TData : struct;

/// <summary>
/// Notifies the <see cref="Changed"/> observable.
/// </summary>
Expand Down Expand Up @@ -496,7 +505,10 @@ internal void NotifyChanged(AvaloniaPropertyChangedEventArgs e)
/// <param name="o">The object instance.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority.</param>
internal abstract void RouteSetValue(
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
internal abstract IDisposable? RouteSetValue(
IAvaloniaObject o,
object value,
BindingPriority priority);
Expand Down
11 changes: 10 additions & 1 deletion src/Avalonia.Base/DirectPropertyBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Avalonia.Data;
using Avalonia.Reactive;
using Avalonia.Utilities;

#nullable enable

Expand Down Expand Up @@ -101,6 +102,12 @@ public TValue GetUnsetValue(Type type)
return (DirectPropertyMetadata<TValue>)base.GetMetadata(type);
}

/// <inheritdoc/>
public override void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
{
vistor.Visit(this, ref data);
}

/// <inheritdoc/>
internal override void RouteClearValue(IAvaloniaObject o)
{
Expand All @@ -114,7 +121,7 @@ internal override void RouteClearValue(IAvaloniaObject o)
}

/// <inheritdoc/>
internal override void RouteSetValue(
internal override IDisposable? RouteSetValue(
IAvaloniaObject o,
object value,
BindingPriority priority)
Expand All @@ -133,6 +140,8 @@ internal override void RouteSetValue(
{
throw v.Error!;
}

return null;
}

/// <inheritdoc/>
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Base/IAvaloniaObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public interface IAvaloniaObject
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
void SetValue<T>(
IDisposable SetValue<T>(
StyledPropertyBase<T> property,
T value,
BindingPriority priority = BindingPriority.LocalValue);
Expand Down
11 changes: 8 additions & 3 deletions src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ namespace Avalonia.PropertyStore
/// <see cref="PriorityValue{T}"/>.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
internal class ConstantValueEntry<T> : IPriorityValueEntry<T>
internal class ConstantValueEntry<T> : IPriorityValueEntry<T>, IDisposable
{
private IValueSink _sink;

public ConstantValueEntry(
StyledPropertyBase<T> property,
T value,
BindingPriority priority)
BindingPriority priority,
IValueSink sink)
{
Property = property;
Value = value;
Priority = priority;
_sink = sink;
}

public StyledPropertyBase<T> Property { get; }
Expand All @@ -28,6 +32,7 @@ public ConstantValueEntry(
Optional<object> IValue.Value => Value.ToObject();
BindingPriority IValue.ValuePriority => Priority;

public void Reparent(IValueSink sink) { }
public void Dispose() => _sink.Completed(Property, this, Value);
public void Reparent(IValueSink sink) => _sink = sink;
}
}
9 changes: 7 additions & 2 deletions src/Avalonia.Base/PropertyStore/PriorityValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,24 @@ public PriorityValue(

public void ClearLocalValue() => UpdateEffectiveValue();

public void SetValue(T value, BindingPriority priority)
public IDisposable? SetValue(T value, BindingPriority priority)
{
IDisposable? result = null;

if (priority == BindingPriority.LocalValue)
{
_localValue = value;
}
else
{
var insert = FindInsertPoint(priority);
_entries.Insert(insert, new ConstantValueEntry<T>(Property, value, priority));
var entry = new ConstantValueEntry<T>(Property, value, priority, this);
_entries.Insert(insert, entry);
result = entry;
}

UpdateEffectiveValue();
return result;
}

public BindingEntry<T> AddBinding(IObservable<BindingValue<T>> source, BindingPriority priority)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public IDisposable Subscribe(IObserver<T> observer)
return this;
}

void IDisposable.Dispose()
public virtual void Dispose()
{
Unsubscribed();
_observer = null;
Expand Down
13 changes: 11 additions & 2 deletions src/Avalonia.Base/StyledPropertyBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using Avalonia.Data;
using Avalonia.Reactive;
using Avalonia.Utilities;

namespace Avalonia
{
Expand Down Expand Up @@ -169,6 +170,12 @@ public void OverrideMetadata(Type type, StyledPropertyMetadata<TValue> metadata)
base.OverrideMetadata(type, metadata);
}

/// <inheritdoc/>
public override void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
{
vistor.Visit(this, ref data);
}

/// <summary>
/// Gets the string representation of the property.
/// </summary>
Expand All @@ -194,7 +201,7 @@ internal override object RouteGetValue(IAvaloniaObject o)
}

/// <inheritdoc/>
internal override void RouteSetValue(
internal override IDisposable RouteSetValue(
IAvaloniaObject o,
object value,
BindingPriority priority)
Expand All @@ -203,7 +210,7 @@ internal override void RouteSetValue(

if (v.HasValue)
{
o.SetValue<TValue>(this, (TValue)v.Value, priority);
return o.SetValue<TValue>(this, (TValue)v.Value, priority);
}
else if (v.Type == BindingValueType.UnsetValue)
{
Expand All @@ -213,6 +220,8 @@ internal override void RouteSetValue(
{
throw v.Error;
}

return null;
}

/// <inheritdoc/>
Expand Down
34 changes: 34 additions & 0 deletions src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#nullable enable

namespace Avalonia.Utilities
{
/// <summary>
/// A visitor to resolve an untyped <see cref="AvaloniaProperty"/> to a typed property.
/// </summary>
/// <typeparam name="TData">The type of user data passed.</typeparam>
/// <remarks>
/// Pass an instance that implements this interface to
/// <see cref="AvaloniaProperty.Accept{TData}(IAvaloniaPropertyVisitor{TData}, ref TData)"/>
/// in order to resolve un untyped <see cref="AvaloniaProperty"/> to a typed
/// <see cref="StyledPropertyBase{TValue}"/> or <see cref="DirectPropertyBase{TValue}"/>.
/// </remarks>
public interface IAvaloniaPropertyVisitor<TData>
where TData : struct
{
/// <summary>
/// Called when the property is a styled property.
/// </summary>
/// <typeparam name="T">The property value type.</typeparam>
/// <param name="property">The property.</param>
/// <param name="data">The user data.</param>
void Visit<T>(StyledPropertyBase<T> property, ref TData data);

/// <summary>
/// Called when the property is a direct property.
/// </summary>
/// <typeparam name="T">The property value type.</typeparam>
/// <param name="property">The property.</param>
/// <param name="data">The user data.</param>
void Visit<T>(DirectPropertyBase<T> property, ref TData data);
}
}
25 changes: 17 additions & 8 deletions src/Avalonia.Base/ValueStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,25 @@ public bool TryGetValue<T>(StyledPropertyBase<T> property, out T value)
return false;
}

public void SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority priority)
public IDisposable? SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority priority)
{
if (property.ValidateValue?.Invoke(value) == false)
{
throw new ArgumentException($"{value} is not a valid value for '{property.Name}.");
}

IDisposable? result = null;

if (_values.TryGetValue(property, out var slot))
{
SetExisting(slot, property, value, priority);
result = SetExisting(slot, property, value, priority);
}
else if (property.HasCoercion)
{
// If the property has any coercion callbacks then always create a PriorityValue.
var entry = new PriorityValue<T>(_owner, property, this);
_values.AddValue(property, entry);
entry.SetValue(value, priority);
result = entry.SetValue(value, priority);
}
else if (priority == BindingPriority.LocalValue)
{
Expand All @@ -95,10 +97,13 @@ public void SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority
}
else
{
var entry = new ConstantValueEntry<T>(property, value, priority);
var entry = new ConstantValueEntry<T>(property, value, priority, this);
_values.AddValue(property, entry);
_sink.ValueChanged(property, priority, default, value);
result = entry;
}

return result;
}

public IDisposable AddBinding<T>(
Expand Down Expand Up @@ -205,21 +210,23 @@ void IValueSink.Completed<T>(
}
}

private void SetExisting<T>(
private IDisposable? SetExisting<T>(
object slot,
StyledPropertyBase<T> property,
T value,
BindingPriority priority)
{
IDisposable? result = null;

if (slot is IPriorityValueEntry<T> e)
{
var priorityValue = new PriorityValue<T>(_owner, property, this, e);
_values.SetValue(property, priorityValue);
priorityValue.SetValue(value, priority);
result = priorityValue.SetValue(value, priority);
}
else if (slot is PriorityValue<T> p)
{
p.SetValue(value, priority);
result = p.SetValue(value, priority);
}
else if (slot is LocalValueEntry<T> l)
{
Expand All @@ -232,14 +239,16 @@ private void SetExisting<T>(
else
{
var priorityValue = new PriorityValue<T>(_owner, property, this, l);
priorityValue.SetValue(value, priority);
result = priorityValue.SetValue(value, priority);
_values.SetValue(property, priorityValue);
}
}
else
{
throw new NotSupportedException("Unrecognised value store slot type.");
}

return result;
}

private IDisposable BindExisting<T>(
Expand Down
Loading

0 comments on commit a70f774

Please sign in to comment.