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