12 changed files with 348 additions and 288 deletions
@ -1,124 +0,0 @@ |
|||||
using System; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using Avalonia.Rendering.Composition.Expressions; |
|
||||
using Avalonia.Rendering.Composition.Server; |
|
||||
using Avalonia.Rendering.Composition.Transport; |
|
||||
using Avalonia.Utilities; |
|
||||
|
|
||||
namespace Avalonia.Rendering.Composition.Animations |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// This is the first element of both animated and non-animated value stores.
|
|
||||
/// It's used to propagate property invalidation to subscribers
|
|
||||
/// </summary>
|
|
||||
|
|
||||
internal struct ServerObjectSubscriptionStore |
|
||||
{ |
|
||||
public bool IsValid; |
|
||||
public RefTrackingDictionary<IAnimationInstance>? Subscribers; |
|
||||
|
|
||||
public void Invalidate() |
|
||||
{ |
|
||||
if (IsValid) |
|
||||
return; |
|
||||
IsValid = false; |
|
||||
if (Subscribers != null) |
|
||||
foreach (var sub in Subscribers) |
|
||||
sub.Key.Invalidate(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The value store for non-animated values that can still be referenced by animations.
|
|
||||
/// Simply stores the value and notifies subscribers
|
|
||||
/// </summary>
|
|
||||
internal struct ServerValueStore<T> |
|
||||
{ |
|
||||
public ServerObjectSubscriptionStore Subscriptions; |
|
||||
private T _value; |
|
||||
public T Value |
|
||||
{ |
|
||||
set |
|
||||
{ |
|
||||
_value = value; |
|
||||
Subscriptions.Invalidate(); |
|
||||
} |
|
||||
get |
|
||||
{ |
|
||||
Subscriptions.IsValid = true; |
|
||||
return _value; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Value store for potentially animated values. Can hold both direct value and animation instance.
|
|
||||
/// Is also responsible for activating/deactivating the animation when container object is activated/deactivated
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T"></typeparam>
|
|
||||
[StructLayout(LayoutKind.Sequential)] |
|
||||
internal struct ServerAnimatedValueStore<T> where T : struct |
|
||||
{ |
|
||||
public ServerObjectSubscriptionStore Subscriptions; |
|
||||
private IAnimationInstance? _animation; |
|
||||
private T _direct; |
|
||||
private T? _lastAnimated; |
|
||||
|
|
||||
public T GetAnimated(ServerCompositor compositor) |
|
||||
{ |
|
||||
Subscriptions.IsValid = true; |
|
||||
if (_animation == null) |
|
||||
return _direct; |
|
||||
var v = _animation.Evaluate(compositor.ServerNow, ExpressionVariant.Create(_direct)) |
|
||||
.CastOrDefault<T>(); |
|
||||
_lastAnimated = v; |
|
||||
return v; |
|
||||
} |
|
||||
|
|
||||
public void Activate(ServerObject parent) |
|
||||
{ |
|
||||
if (_animation != null) |
|
||||
_animation.Activate(); |
|
||||
} |
|
||||
|
|
||||
public void Deactivate(ServerObject parent) |
|
||||
{ |
|
||||
if (_animation != null) |
|
||||
_animation.Deactivate(); |
|
||||
} |
|
||||
|
|
||||
private T LastAnimated => _animation != null ? _lastAnimated ?? _direct : _direct; |
|
||||
|
|
||||
public bool IsAnimation => _animation != null; |
|
||||
|
|
||||
public void SetAnimation(ServerObject target, TimeSpan commitedAt, IAnimationInstance animation, int storeOffset) |
|
||||
{ |
|
||||
_direct = default; |
|
||||
if (_animation != null) |
|
||||
{ |
|
||||
if (target.IsActive) |
|
||||
_animation.Deactivate(); |
|
||||
} |
|
||||
|
|
||||
_animation = animation; |
|
||||
_animation.Initialize(commitedAt, ExpressionVariant.Create(LastAnimated), storeOffset); |
|
||||
if (target.IsActive) |
|
||||
_animation.Activate(); |
|
||||
|
|
||||
Subscriptions.Invalidate(); |
|
||||
} |
|
||||
|
|
||||
public void SetValue(ServerObject target, T value) |
|
||||
{ |
|
||||
if (_animation != null) |
|
||||
{ |
|
||||
if (target.IsActive) |
|
||||
_animation.Deactivate(); |
|
||||
} |
|
||||
|
|
||||
_animation = null; |
|
||||
_direct = value; |
|
||||
Subscriptions.Invalidate(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,15 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace Avalonia.Rendering.Composition.Server; |
||||
|
|
||||
|
internal class CompositionProperty |
||||
|
{ |
||||
|
private static volatile int s_NextId = 1; |
||||
|
public int Id { get; private set; } |
||||
|
|
||||
|
public static CompositionProperty Register() => new() |
||||
|
{ |
||||
|
Id = Interlocked.Increment(ref s_NextId) |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,181 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
|
||||
|
namespace Avalonia.Utilities; |
||||
|
|
||||
|
public struct InlineDictionary<TKey, TValue> where TKey : class where TValue : class |
||||
|
{ |
||||
|
object? _data; |
||||
|
TValue? _value; |
||||
|
|
||||
|
void SetCore(TKey key, TValue value, bool overwrite) |
||||
|
{ |
||||
|
if (_data == null) |
||||
|
{ |
||||
|
_data = key; |
||||
|
_value = value; |
||||
|
} |
||||
|
else if (_data is KeyValuePair<TKey?, TValue?>[] arr) |
||||
|
{ |
||||
|
var free = -1; |
||||
|
for (var c = 0; c < arr.Length; c++) |
||||
|
{ |
||||
|
if (arr[c].Key == key) |
||||
|
{ |
||||
|
if (overwrite) |
||||
|
{ |
||||
|
arr[c] = new(key, value); |
||||
|
return; |
||||
|
} |
||||
|
else |
||||
|
throw new ArgumentException("Key already exists in dictionary"); |
||||
|
} |
||||
|
|
||||
|
if (arr[c].Key == null) |
||||
|
free = c; |
||||
|
} |
||||
|
|
||||
|
if (free != -1) |
||||
|
{ |
||||
|
arr[free] = new KeyValuePair<TKey?, TValue?>(key, value); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Upgrade to dictionary
|
||||
|
var newDic = new Dictionary<TKey, TValue?>(); |
||||
|
foreach (var kvp in arr) |
||||
|
newDic.Add(kvp.Key!, kvp.Value!); |
||||
|
newDic.Add(key, value); |
||||
|
_data = newDic; |
||||
|
} |
||||
|
else if (_data is Dictionary<TKey, TValue?> dic) |
||||
|
{ |
||||
|
if (overwrite) |
||||
|
dic[key] = value; |
||||
|
else |
||||
|
dic.Add(key, value); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// We have a single element, upgrade to array
|
||||
|
arr = new KeyValuePair<TKey?, TValue?>[6]; |
||||
|
arr[0] = new KeyValuePair<TKey?, TValue?>((TKey)_data, _value); |
||||
|
arr[1] = new KeyValuePair<TKey?, TValue?>(key, value); |
||||
|
_data = arr; |
||||
|
_value = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Add(TKey key, TValue value) => SetCore(key, value, false); |
||||
|
public void Set(TKey key, TValue value) => SetCore(key, value, true); |
||||
|
|
||||
|
public TValue this[TKey key] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (TryGetValue(key, out var rv)) |
||||
|
return rv; |
||||
|
throw new KeyNotFoundException(); |
||||
|
} |
||||
|
set => Set(key, value); |
||||
|
} |
||||
|
|
||||
|
public bool Remove(TKey key) |
||||
|
{ |
||||
|
if (_data == key) |
||||
|
{ |
||||
|
_data = null; |
||||
|
_value = null; |
||||
|
return true; |
||||
|
} |
||||
|
else if (_data is KeyValuePair<TKey?, TValue?>[] arr) |
||||
|
{ |
||||
|
for (var c = 0; c < arr.Length; c++) |
||||
|
{ |
||||
|
if (arr[c].Key == key) |
||||
|
{ |
||||
|
arr[c] = default; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
else if (_data is Dictionary<TKey, TValue?> dic) |
||||
|
return dic.Remove(key); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public bool TryGetValue(TKey key, [MaybeNullWhen(false)]out TValue value) |
||||
|
{ |
||||
|
if (_data == key) |
||||
|
{ |
||||
|
value = _value!; |
||||
|
return true; |
||||
|
} |
||||
|
else if (_data is KeyValuePair<TKey?, TValue?>[] arr) |
||||
|
{ |
||||
|
for (var c = 0; c < arr.Length; c++) |
||||
|
{ |
||||
|
if (arr[c].Key == key) |
||||
|
{ |
||||
|
value = arr[c].Value!; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
value = null; |
||||
|
return false; |
||||
|
} |
||||
|
else if (_data is Dictionary<TKey, TValue?> dic) |
||||
|
return dic.TryGetValue(key, out value); |
||||
|
|
||||
|
value = null; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public bool TryGetAndRemoveValue(TKey key, [MaybeNullWhen(false)]out TValue value) |
||||
|
{ |
||||
|
if (_data == key) |
||||
|
{ |
||||
|
value = _value!; |
||||
|
_value = null; |
||||
|
_data = null; |
||||
|
return true; |
||||
|
} |
||||
|
else if (_data is KeyValuePair<TKey?, TValue?>[] arr) |
||||
|
{ |
||||
|
for (var c = 0; c < arr.Length; c++) |
||||
|
{ |
||||
|
if (arr[c].Key == key) |
||||
|
{ |
||||
|
value = arr[c].Value!; |
||||
|
arr[c] = default; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
value = null; |
||||
|
return false; |
||||
|
} |
||||
|
else if (_data is Dictionary<TKey, TValue?> dic) |
||||
|
{ |
||||
|
if (!dic.TryGetValue(key, out value)) |
||||
|
return false; |
||||
|
dic.Remove(key); |
||||
|
} |
||||
|
|
||||
|
value = null; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public TValue GetAndRemove(TKey key) |
||||
|
{ |
||||
|
if (TryGetAndRemoveValue(key, out var v)) |
||||
|
return v; |
||||
|
throw new KeyNotFoundException(); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue