Browse Source

Merge branch 'master' into update-and-fix-reactiveui

pull/8738/head
Dan Walmsley 4 years ago
committed by GitHub
parent
commit
be474dfef8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      build/HarfBuzzSharp.props
  2. 6
      build/SkiaSharp.props
  3. 45
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  4. 17
      src/Avalonia.Base/Animation/Animation.cs
  5. 13
      src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs
  6. 35
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  7. 15
      src/Avalonia.Base/AvaloniaProperty`1.cs
  8. 15
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  9. 15
      src/Avalonia.Base/DirectPropertyBase.cs
  10. 4
      src/Avalonia.Base/EnumExtensions.cs
  11. 18
      src/Avalonia.Base/Interactivity/RoutedEvent.cs
  12. 11
      src/Avalonia.Base/Layout/ILayoutManager.cs
  13. 11
      src/Avalonia.Base/Layout/LayoutManager.cs
  14. 28
      src/Avalonia.Base/Layout/Layoutable.cs
  15. 16
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  16. 14
      src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
  17. 35
      src/Avalonia.Base/Utilities/MathUtilities.cs
  18. 25
      src/Avalonia.Base/Utilities/WeakObservable.cs
  19. 193
      src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs
  20. 16
      src/Avalonia.Base/Visual.cs
  21. 19
      src/Avalonia.Base/VisualTree/IVisualTreeHost.cs
  22. 30
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  23. 4
      src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
  24. 9
      src/Avalonia.Controls/Primitives/Popup.cs
  25. 2
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  26. 36
      src/Avalonia.Controls/Shapes/Arc.cs
  27. 97
      src/Avalonia.Controls/Shapes/Sector.cs
  28. 48
      src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
  29. 2
      src/Avalonia.X11/X11IconLoader.cs
  30. 155
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
  31. 70
      tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs
  32. 2
      tests/Avalonia.RenderTests/Media/BitmapTests.cs

6
build/HarfBuzzSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2" />
<PackageReference Include="HarfBuzzSharp" Version="2.8.2.1-preview.108" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.108" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.1-preview.108" />
</ItemGroup>
</Project>

6
build/SkiaSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.1" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.1" />
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.108" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.108" />
</ItemGroup>
</Project>

45
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@ -27,46 +27,33 @@ namespace Avalonia.Android
{
if (interval.TotalMilliseconds < 10)
interval = TimeSpan.FromMilliseconds(10);
object l = new object();
var stopped = false;
Timer timer = null;
var scheduled = false;
timer = new Timer(_ =>
{
lock (l)
if (stopped)
return;
EnsureInvokeOnMainThread(() =>
{
if (stopped)
try
{
timer.Dispose();
return;
tick();
}
if (scheduled)
return;
scheduled = true;
EnsureInvokeOnMainThread(() =>
finally
{
try
{
tick();
}
finally
{
lock (l)
{
scheduled = false;
}
}
});
}
}, null, TimeSpan.Zero, interval);
if (!stopped)
timer.Change(interval, Timeout.InfiniteTimeSpan);
}
});
},
null, interval, Timeout.InfiniteTimeSpan);
return Disposable.Create(() =>
{
lock (l)
{
stopped = true;
timer.Dispose();
}
stopped = true;
timer.Dispose();
});
}

17
src/Avalonia.Base/Animation/Animation.cs

@ -172,23 +172,6 @@ namespace Avalonia.Animation
set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); }
}
/// <summary>
/// Obsolete: Do not use this property, use <see cref="IterationCount"/> instead.
/// </summary>
/// <value></value>
[Obsolete("This property has been superceded by IterationCount.")]
public string RepeatCount
{
get { return IterationCount.ToString(); }
set
{
var val = value.ToUpper();
val = val.Replace("LOOP", "INFINITE");
val = val.Replace("NONE", "1");
IterationCount = IterationCount.Parse(val);
}
}
/// <summary>
/// Gets the children of the <see cref="Animation"/>.
/// </summary>

13
src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs

@ -37,17 +37,4 @@ namespace Avalonia.Animation.Animators
}
}
[Obsolete("Use ISolidColorBrushAnimator instead")]
public class SolidColorBrushAnimator : Animator<SolidColorBrush?>
{
public override SolidColorBrush? Interpolate(double progress, SolidColorBrush? oldValue, SolidColorBrush? newValue)
{
if (oldValue is null || newValue is null)
{
return progress >= 0.5 ? newValue : oldValue;
}
return new SolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
}
}
}

35
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -468,41 +468,6 @@ namespace Avalonia
});
}
/// <summary>
/// Subscribes to a property changed notifications for changes that originate from a
/// <typeparamref name="TTarget"/>.
/// </summary>
/// <typeparam name="TTarget">The type of the property change sender.</typeparam>
/// <param name="observable">The property changed observable.</param>
/// <param name="handler">Given a TTarget, returns the handler.</param>
/// <returns>A disposable that can be used to terminate the subscription.</returns>
[Obsolete("Use overload taking Action<TTarget, AvaloniaPropertyChangedEventArgs>.")]
public static IDisposable AddClassHandler<TTarget>(
this IObservable<AvaloniaPropertyChangedEventArgs> observable,
Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler)
where TTarget : class
{
return observable.Subscribe(e => SubscribeAdapter(e, handler));
}
/// <summary>
/// Observer method for <see cref="AddClassHandler{TTarget}(IObservable{AvaloniaPropertyChangedEventArgs},
/// Func{TTarget, Action{AvaloniaPropertyChangedEventArgs}})"/>.
/// </summary>
/// <typeparam name="TTarget">The sender type to accept.</typeparam>
/// <param name="e">The event args.</param>
/// <param name="handler">Given a TTarget, returns the handler.</param>
private static void SubscribeAdapter<TTarget>(
AvaloniaPropertyChangedEventArgs e,
Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler)
where TTarget : class
{
if (e.Sender is TTarget target)
{
handler(target)(e);
}
}
private class BindingAdaptor : IBinding
{
private IObservable<object?> _source;

15
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -30,21 +30,6 @@ namespace Avalonia
_changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with AvaloniaProperty<TValue> instead.", true)]
protected AvaloniaProperty(
AvaloniaProperty source,
Type ownerType,
AvaloniaPropertyMetadata? metadata)
: this(source as AvaloniaProperty<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary>

15
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@ -140,21 +140,6 @@ namespace Avalonia.Collections
}
}
[Obsolete("Causes memory leaks. Use DynamicData or similar instead.")]
public static IAvaloniaReadOnlyList<TDerived> CreateDerivedList<TSource, TDerived>(
this IAvaloniaReadOnlyList<TSource> collection,
Func<TSource, TDerived> select)
{
var result = new AvaloniaList<TDerived>();
collection.ForEachItem(
(i, item) => result.Insert(i, select(item)),
(i, item) => result.RemoveAt(i),
() => result.Clear());
return result;
}
/// <summary>
/// Listens for property changed events from all items in a collection.
/// </summary>

15
src/Avalonia.Base/DirectPropertyBase.cs

@ -29,21 +29,6 @@ namespace Avalonia
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with DirectPropertyBase<TValue> instead.", true)]
protected DirectPropertyBase(
AvaloniaProperty source,
Type ownerType,
AvaloniaPropertyMetadata metadata)
: this(source as DirectPropertyBase<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary>

4
src/Avalonia.Base/EnumExtensions.cs

@ -8,10 +8,6 @@ namespace Avalonia
/// </summary>
public static class EnumExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Obsolete("This method is obsolete. Use HasAllFlags instead.")]
public static bool HasFlagCustom<T>(this T value, T flag) where T : unmanaged, Enum
=> value.HasAllFlags(flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool HasAllFlags<T>(this T value, T flags) where T : unmanaged, Enum

18
src/Avalonia.Base/Interactivity/RoutedEvent.cs

@ -113,24 +113,6 @@ namespace Avalonia.Interactivity
{
}
[Obsolete("Use overload taking Action<TTarget, TEventArgs>.")]
public IDisposable AddClassHandler<TTarget>(
Func<TTarget, Action<TEventArgs>> handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false)
where TTarget : class, IInteractive
{
void Adapter(object? sender, RoutedEventArgs e)
{
if (sender is TTarget target && e is TEventArgs args)
{
handler(target)(args);
}
}
return AddClassHandler(typeof(TTarget), Adapter, routes, handledEventsToo);
}
public IDisposable AddClassHandler<TTarget>(
Action<TTarget, TEventArgs> handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,

11
src/Avalonia.Base/Layout/ILayoutManager.cs

@ -44,17 +44,6 @@ namespace Avalonia.Layout
/// </remarks>
void ExecuteInitialLayoutPass();
/// <summary>
/// Executes the initial layout pass on a layout root.
/// </summary>
/// <param name="root">The control to lay out.</param>
/// <remarks>
/// You should not usually need to call this method explictly, the layout root will call
/// it to carry out the initial layout of the control.
/// </remarks>
[Obsolete("Call ExecuteInitialLayoutPass without parameter")]
void ExecuteInitialLayoutPass(ILayoutRoot root);
/// <summary>
/// Registers a control as wanting to receive effective viewport notifications.
/// </summary>

11
src/Avalonia.Base/Layout/LayoutManager.cs

@ -196,17 +196,6 @@ namespace Avalonia.Layout
ExecuteLayoutPass();
}
[Obsolete("Call ExecuteInitialLayoutPass without parameter")]
public void ExecuteInitialLayoutPass(ILayoutRoot root)
{
if (root != _owner)
{
throw new ArgumentException("ExecuteInitialLayoutPass called with incorrect root.");
}
ExecuteInitialLayoutPass();
}
public void Dispose()
{
_disposed = true;

28
src/Avalonia.Base/Layout/Layoutable.cs

@ -460,20 +460,6 @@ namespace Avalonia.Layout
_effectiveViewportChanged?.Invoke(this, e);
}
/// <summary>
/// Marks a property as affecting the control's measurement.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateMeasure"/> to be called on the element.
/// </remarks>
[Obsolete("Use AffectsMeasure<T> and specify the control type.")]
protected static void AffectsMeasure(params AvaloniaProperty[] properties)
{
AffectsMeasure<Layoutable>(properties);
}
/// <summary>
/// Marks a property as affecting the control's measurement.
/// </summary>
@ -497,20 +483,6 @@ namespace Avalonia.Layout
}
}
/// <summary>
/// Marks a property as affecting the control's arrangement.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateArrange"/> to be called on the element.
/// </remarks>
[Obsolete("Use AffectsArrange<T> and specify the control type.")]
protected static void AffectsArrange(params AvaloniaProperty[] properties)
{
AffectsArrange<Layoutable>(properties);
}
/// <summary>
/// Marks a property as affecting the control's arrangement.
/// </summary>

16
src/Avalonia.Base/Media/Imaging/Bitmap.cs

@ -89,22 +89,6 @@ namespace Avalonia.Media.Imaging
PlatformImpl.Dispose();
}
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
/// <param name="format">The pixel format.</param>
/// <param name="data">The pointer to the source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
[Obsolete("Use overload taking an AlphaFormat.")]
public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{
var ri = GetFactory();
PlatformImpl = RefCountable.Create(ri
.LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride));
}
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>

14
src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs

@ -9,18 +9,6 @@ namespace Avalonia.Media.Imaging
/// </summary>
public class WriteableBitmap : Bitmap
{
/// <summary>
/// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
/// </summary>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
[Obsolete("Use overload taking an AlphaFormat.")]
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
: base(CreatePlatformImpl(size, dpi, format, null))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
@ -30,7 +18,7 @@ namespace Avalonia.Media.Imaging
/// <param name="format">The pixel format (optional).</param>
/// <param name="alphaFormat">The alpha format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null)
: base(CreatePlatformImpl(size, dpi, format, alphaFormat))
{
}

35
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -324,6 +324,41 @@ namespace Avalonia.Utilities
return angle * 2 * Math.PI;
}
/// <summary>
/// Calculates the point of an angle on an ellipse.
/// </summary>
/// <param name="centre">The centre point of the ellipse.</param>
/// <param name="radiusX">The x radius of the ellipse.</param>
/// <param name="radiusY">The y radius of the ellipse.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>A point on the ellipse.</returns>
public static Point GetEllipsePoint(Point centre, double radiusX, double radiusY, double angle)
{
return new Point(radiusX * Math.Cos(angle) + centre.X, radiusY * Math.Sin(angle) + centre.Y);
}
/// <summary>
/// Gets the minimum and maximum from the specified numbers.
/// </summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>A tuple containing the minimum and maximum of the two specified numbers.</returns>
public static (double min, double max) GetMinMax(double a, double b)
{
return a < b ? (a, b) : (b, a);
}
/// <summary>
/// Gets the minimum and maximum from the specified number and the difference with that number.
/// </summary>
/// <param name="initialValue">The initial value to use.</param>
/// <param name="delta">The difference for <paramref name="initialValue"/>.</param>
/// <returns>A tuple containing the minimum and maximum of the specified number and the difference with that number.</returns>
public static (double min, double max) GetMinMaxFromDelta(double initialValue, double delta)
{
return GetMinMax(initialValue, initialValue + delta);
}
private static void ThrowCannotBeGreaterThanException<T>(T min, T max)
{
throw new ArgumentException($"{min} cannot be greater than {max}.");

25
src/Avalonia.Base/Utilities/WeakObservable.cs

@ -9,31 +9,6 @@ namespace Avalonia.Utilities
/// </summary>
public static class WeakObservable
{
/// <summary>
/// Converts a .NET event conforming to the standard .NET event pattern into an observable
/// sequence, subscribing weakly.
/// </summary>
/// <typeparam name="TTarget">The type of target.</typeparam>
/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
/// <param name="target">Object instance that exposes the event to convert.</param>
/// <param name="eventName">Name of the event to convert.</param>
/// <returns></returns>
[Obsolete("Use WeakEvent-based overload")]
public static IObservable<EventPattern<object, TEventArgs>> FromEventPattern<TTarget, TEventArgs>(
TTarget target,
string eventName)
where TEventArgs : EventArgs
{
_ = target ?? throw new ArgumentNullException(nameof(target));
_ = eventName ?? throw new ArgumentNullException(nameof(eventName));
return Observable.Create<EventPattern<object, TEventArgs>>(observer =>
{
var handler = new Handler<TEventArgs>(observer);
WeakSubscriptionManager.Subscribe(target, eventName, handler);
return () => WeakSubscriptionManager.Unsubscribe(target, eventName, handler);
}).Publish().RefCount();
}
private class Handler<TEventArgs>
: IWeakSubscriber<TEventArgs>,

193
src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs

@ -1,193 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Avalonia.Utilities
{
/// <summary>
/// Manages subscriptions to events using weak listeners.
/// </summary>
public static class WeakSubscriptionManager
{
/// <summary>
/// Subscribes to an event on an object using a weak subscription.
/// </summary>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
/// <param name="target">The event source.</param>
/// <param name="eventName">The name of the event.</param>
/// <param name="subscriber">The subscriber.</param>
[Obsolete("Use WeakEvent")]
public static void Subscribe<TTarget, TEventArgs>(TTarget target, string eventName, IWeakSubscriber<TEventArgs> subscriber)
where TEventArgs : EventArgs
{
_ = target ?? throw new ArgumentNullException(nameof(target));
var dic = SubscriptionTypeStorage<TEventArgs>.Subscribers.GetOrCreateValue(target);
if (!dic.TryGetValue(eventName, out var sub))
{
dic[eventName] = sub = new Subscription<TEventArgs>(dic, typeof(TTarget), target, eventName);
}
sub.Add(new WeakReference<IWeakSubscriber<TEventArgs>>(subscriber));
}
/// <summary>
/// Unsubscribes from an event.
/// </summary>
/// <typeparam name="T">The type of the event arguments.</typeparam>
/// <param name="target">The event source.</param>
/// <param name="eventName">The name of the event.</param>
/// <param name="subscriber">The subscriber.</param>
public static void Unsubscribe<T>(object target, string eventName, IWeakSubscriber<T> subscriber)
where T : EventArgs
{
if (SubscriptionTypeStorage<T>.Subscribers.TryGetValue(target, out var dic))
{
if (dic.TryGetValue(eventName, out var sub))
{
sub.Remove(subscriber);
}
}
}
private static class SubscriptionTypeStorage<T>
where T : EventArgs
{
public static readonly ConditionalWeakTable<object, SubscriptionDic<T>> Subscribers
= new ConditionalWeakTable<object, SubscriptionDic<T>>();
}
private class SubscriptionDic<T> : Dictionary<string, Subscription<T>>
where T : EventArgs
{
}
private static readonly Dictionary<Type, Dictionary<string, EventInfo>> Accessors
= new Dictionary<Type, Dictionary<string, EventInfo>>();
private class Subscription<T> where T : EventArgs
{
private readonly EventInfo _info;
private readonly SubscriptionDic<T> _sdic;
private readonly object _target;
private readonly string _eventName;
private readonly Delegate _delegate;
private WeakReference<IWeakSubscriber<T>>?[] _data = new WeakReference<IWeakSubscriber<T>>?[16];
private int _count = 0;
public Subscription(SubscriptionDic<T> sdic, Type targetType, object target, string eventName)
{
_sdic = sdic;
_target = target;
_eventName = eventName;
if (!Accessors.TryGetValue(targetType, out var evDic))
Accessors[targetType] = evDic = new Dictionary<string, EventInfo>();
if (evDic.TryGetValue(eventName, out var info))
{
_info = info;
}
else
{
var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName);
if (ev == null)
{
throw new ArgumentException(
$"The event {eventName} was not found on {target.GetType()}.");
}
evDic[eventName] = _info = ev;
}
var del = new Action<object, T>(OnEvent);
_delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType!, del.Target);
_info.AddMethod!.Invoke(target, new[] { _delegate });
}
void Destroy()
{
_info.RemoveMethod!.Invoke(_target, new[] { _delegate });
_sdic.Remove(_eventName);
}
public void Add(WeakReference<IWeakSubscriber<T>> s)
{
if (_count == _data.Length)
{
//Extend capacity
var ndata = new WeakReference<IWeakSubscriber<T>>?[_data.Length*2];
Array.Copy(_data, ndata, _data.Length);
_data = ndata;
}
_data[_count] = s!;
_count++;
}
public void Remove(IWeakSubscriber<T> s)
{
var removed = false;
for (int c = 0; c < _count; ++c)
{
var reference = _data[c];
IWeakSubscriber<T>? instance;
if (reference != null && reference.TryGetTarget(out instance) && instance == s)
{
_data[c] = null;
removed = true;
}
}
if (removed)
{
Compact();
}
}
void Compact()
{
int empty = -1;
for (int c = 0; c < _count; c++)
{
var r = _data[c];
//Mark current index as first empty
if (r == null && empty == -1)
empty = c;
//If current element isn't null and we have an empty one
if (r != null && empty != -1)
{
_data[c] = null;
_data[empty] = r;
empty++;
}
}
if (empty != -1)
_count = empty;
if (_count == 0)
Destroy();
}
void OnEvent(object sender, T eventArgs)
{
var needCompact = false;
for(var c=0; c<_count; c++)
{
var r = _data[c];
if (r?.TryGetTarget(out var sub) == true)
sub!.OnEvent(sender, eventArgs);
else
needCompact = true;
}
if (needCompact)
Compact();
}
}
}
}

16
src/Avalonia.Base/Visual.cs

@ -338,22 +338,6 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(context != null);
}
/// <summary>
/// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be
/// called.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// This method should be called in a control's static constructor with each property
/// on the control which when changed should cause a redraw. This is similar to WPF's
/// FrameworkPropertyMetadata.AffectsRender flag.
/// </remarks>
[Obsolete("Use AffectsRender<T> and specify the control type.")]
protected static void AffectsRender(params AvaloniaProperty[] properties)
{
AffectsRender<Visual>(properties);
}
/// <summary>
/// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be
/// called.

19
src/Avalonia.Base/VisualTree/IVisualTreeHost.cs

@ -1,19 +0,0 @@
using System;
namespace Avalonia.VisualTree
{
/// <summary>
/// Interface for controls that host their own separate visual tree, such as popups.
/// </summary>
[Obsolete]
public interface IVisualTreeHost
{
/// <summary>
/// Gets the root of the hosted visual tree.
/// </summary>
/// <value>
/// The root of the hosted visual tree.
/// </value>
IVisual? Root { get; }
}
}

30
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -2167,7 +2167,23 @@ namespace Avalonia.Controls
return desiredSize;
}
/// <inheritdoc/>
protected override void OnDataContextBeginUpdate()
{
base.OnDataContextBeginUpdate();
NotifyDataContextPropertyForAllRowCells(GetAllRows(), true);
}
/// <inheritdoc/>
protected override void OnDataContextEndUpdate()
{
base.OnDataContextEndUpdate();
NotifyDataContextPropertyForAllRowCells(GetAllRows(), false);
}
/// <summary>
/// Raises the BeginningEdit event.
/// </summary>
@ -3165,6 +3181,20 @@ namespace Avalonia.Controls
}
}
private static void NotifyDataContextPropertyForAllRowCells(IEnumerable<DataGridRow> rowSource, bool arg2)
{
foreach (DataGridRow row in rowSource)
{
foreach (DataGridCell cell in row.Cells)
{
if (cell.Content is StyledElement cellContent)
{
DataContextProperty.Notifying?.Invoke(cellContent, arg2);
}
}
}
}
private void UpdateRowDetailsVisibilityMode(DataGridRowDetailsVisibilityMode newDetailsMode)
{
int itemCount = DataConnection.Count;

4
src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs

@ -43,7 +43,7 @@ namespace Avalonia.Controls.Platform
_priority = priority;
_interval = interval;
_tick = tick;
_timer = new Timer(OnTimer, null, interval, TimeSpan.FromMilliseconds(-1));
_timer = new Timer(OnTimer, null, interval, Timeout.InfiniteTimeSpan);
_handle = GCHandle.Alloc(_timer);
}
@ -57,7 +57,7 @@ namespace Avalonia.Controls.Platform
if (_timer == null)
return;
_tick();
_timer?.Change(_interval, TimeSpan.FromMilliseconds(-1));
_timer?.Change(_interval, Timeout.InfiniteTimeSpan);
});
}

9
src/Avalonia.Controls/Primitives/Popup.cs

@ -22,9 +22,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Displays a popup window.
/// </summary>
#pragma warning disable CS0612 // Type or member is obsolete
public class Popup : Control, IVisualTreeHost, IPopupHostProvider
#pragma warning restore CS0612 // Type or member is obsolete
public class Popup : Control, IPopupHostProvider
{
public static readonly StyledProperty<bool> WindowManagerAddShadowHintProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(WindowManagerAddShadowHint), false);
@ -372,11 +370,6 @@ namespace Avalonia.Controls.Primitives
set { SetValue(TopmostProperty, value); }
}
/// <summary>
/// Gets the root of the popup window.
/// </summary>
IVisual? IVisualTreeHost.Root => _openState?.PopupHost.HostedVisualTreeRoot;
IPopupHost? IPopupHostProvider.PopupHost => Host;
event Action<IPopupHost?>? IPopupHostProvider.PopupHostChanged

2
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -77,10 +77,8 @@ namespace Avalonia.Controls.Remote
_bitmap.PixelSize.Height != _lastFrame.Height)
{
_bitmap?.Dispose();
#pragma warning disable CS0618 // Type or member is obsolete
_bitmap = new WriteableBitmap(new PixelSize(_lastFrame.Width, _lastFrame.Height),
new Vector(96, 96), fmt);
#pragma warning restore CS0618 // Type or member is obsolete
}
using (var l = _bitmap.Lock())
{

36
src/Avalonia.Controls/Shapes/Arc.cs

@ -1,8 +1,12 @@
using System;
using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls.Shapes
{
/// <summary>
/// Represents a circular or elliptical arc (a segment of a curve).
/// </summary>
public class Arc : Shape
{
/// <summary>
@ -19,8 +23,12 @@ namespace Avalonia.Controls.Shapes
static Arc()
{
StrokeThicknessProperty.OverrideDefaultValue<Arc>(1);
AffectsGeometry<Arc>(BoundsProperty, StrokeThicknessProperty, StartAngleProperty, SweepAngleProperty);
StrokeThicknessProperty.OverrideDefaultValue<Arc>(1.0d);
AffectsGeometry<Arc>(
BoundsProperty,
StrokeThicknessProperty,
StartAngleProperty,
SweepAngleProperty);
}
/// <summary>
@ -42,10 +50,11 @@ namespace Avalonia.Controls.Shapes
set => SetValue(SweepAngleProperty, value);
}
/// <inheritdoc/>
protected override Geometry CreateDefiningGeometry()
{
var angle1 = DegreesToRad(StartAngle);
var angle2 = angle1 + DegreesToRad(SweepAngle);
var angle1 = MathUtilities.Deg2Rad(StartAngle);
var angle2 = angle1 + MathUtilities.Deg2Rad(SweepAngle);
var startAngle = Math.Min(angle1, angle2);
var sweepAngle = Math.Max(angle1, angle2);
@ -80,24 +89,25 @@ namespace Avalonia.Controls.Shapes
var arcGeometry = new StreamGeometry();
using (var ctx = arcGeometry.Open())
using (StreamGeometryContext context = arcGeometry.Open())
{
ctx.BeginFigure(startPoint, false);
ctx.ArcTo(endPoint, new Size(radiusX, radiusY), angleGap, angleGap >= Math.PI,
context.BeginFigure(startPoint, false);
context.ArcTo(
endPoint,
new Size(radiusX, radiusY),
rotationAngle: angleGap,
isLargeArc: angleGap >= Math.PI,
SweepDirection.Clockwise);
ctx.EndFigure(false);
context.EndFigure(false);
}
return arcGeometry;
}
}
static double DegreesToRad(double inAngle) =>
inAngle * Math.PI / 180;
private static double RadToNormRad(double inAngle) => ((inAngle % (Math.PI * 2)) + (Math.PI * 2)) % (Math.PI * 2);
static double RadToNormRad(double inAngle) => ((inAngle % (Math.PI * 2)) + (Math.PI * 2)) % (Math.PI * 2);
static Point GetRingPoint(double radiusX, double radiusY, double centerX, double centerY, double angle) =>
private static Point GetRingPoint(double radiusX, double radiusY, double centerX, double centerY, double angle) =>
new Point((radiusX * Math.Cos(angle)) + centerX, (radiusY * Math.Sin(angle)) + centerY);
}
}

97
src/Avalonia.Controls/Shapes/Sector.cs

@ -0,0 +1,97 @@
using System;
using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls.Shapes
{
/// <summary>
/// Represents a circular or elliptical sector (a pie-shaped closed region of a circle or ellipse).
/// </summary>
public class Sector : Shape
{
/// <summary>
/// Defines the <see cref="StartAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> StartAngleProperty =
AvaloniaProperty.Register<Sector, double>(nameof(StartAngle), 0.0d);
/// <summary>
/// Defines the <see cref="SweepAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> SweepAngleProperty =
AvaloniaProperty.Register<Sector, double>(nameof(SweepAngle), 0.0d);
/// <summary>
/// Gets or sets the angle at which the sector's arc starts, in degrees.
/// </summary>
public double StartAngle
{
get => GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
/// <summary>
/// Gets or sets the angle, in degrees, added to the <see cref="StartAngle"/> defining where the sector's arc ends.
/// A positive value is clockwise, negative is counter-clockwise.
/// </summary>
public double SweepAngle
{
get => GetValue(SweepAngleProperty);
set => SetValue(SweepAngleProperty, value);
}
static Sector()
{
StrokeThicknessProperty.OverrideDefaultValue<Sector>(1.0d);
AffectsGeometry<Sector>(
BoundsProperty,
StrokeThicknessProperty,
StartAngleProperty,
SweepAngleProperty);
}
/// <inheritdoc/>
protected override Geometry? CreateDefiningGeometry()
{
Rect rect = new Rect(Bounds.Size);
Rect deflatedRect = rect.Deflate(StrokeThickness * 0.5d);
if (SweepAngle >= 360.0d || SweepAngle <= -360.0d)
{
return new EllipseGeometry(deflatedRect);
}
if (SweepAngle == 0.0d)
{
return new StreamGeometry();
}
(double startAngle, double endAngle) = MathUtilities.GetMinMaxFromDelta(
MathUtilities.Deg2Rad(StartAngle),
MathUtilities.Deg2Rad(SweepAngle));
Point centre = new Point(rect.Width * 0.5d, rect.Height * 0.5d);
double radiusX = deflatedRect.Width * 0.5d;
double radiusY = deflatedRect.Height * 0.5d;
Point startCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, startAngle);
Point endCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, endAngle);
Size size = new Size(radiusX, radiusY);
var streamGeometry = new StreamGeometry();
using (StreamGeometryContext context = streamGeometry.Open())
{
context.BeginFigure(startCurvePoint, false);
context.ArcTo(
endCurvePoint,
size,
rotationAngle: 0.0d,
isLargeArc: Math.Abs(SweepAngle) > 180.0d,
SweepDirection.Clockwise);
context.LineTo(centre);
context.EndFigure(true);
}
return streamGeometry;
}
}
}

48
src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs

@ -36,35 +36,35 @@ namespace Avalonia.Headless
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{
var cancelled = false;
var enqueued = false;
var l = new object();
var timer = new Timer(_ =>
if (interval.TotalMilliseconds < 10)
interval = TimeSpan.FromMilliseconds(10);
var stopped = false;
Timer timer = null;
timer = new Timer(_ =>
{
lock (l)
if (stopped)
return;
Dispatcher.UIThread.Post(() =>
{
if (cancelled || enqueued)
return;
enqueued = true;
Dispatcher.UIThread.Post(() =>
try
{
lock (l)
{
enqueued = false;
if (cancelled)
return;
tick();
}
}, priority);
}
}, null, interval, interval);
tick();
}
finally
{
if (!stopped)
timer.Change(interval, Timeout.InfiniteTimeSpan);
}
});
},
null, interval, Timeout.InfiniteTimeSpan);
return Disposable.Create(() =>
{
lock (l)
{
timer.Dispose();
cancelled = true;
}
stopped = true;
timer.Dispose();
});
}

2
src/Avalonia.X11/X11IconLoader.cs

@ -68,9 +68,7 @@ namespace Avalonia.X11
public void Save(Stream outputStream)
{
using (var wr =
#pragma warning disable CS0618 // Type or member is obsolete
new WriteableBitmap(new PixelSize(_width, _height), new Vector(96, 96), PixelFormat.Bgra8888))
#pragma warning restore CS0618 // Type or member is obsolete
{
using (var fb = wr.Lock())
{

155
tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs

@ -1,155 +0,0 @@
using System.Linq;
using Avalonia.Collections;
using Xunit;
namespace Avalonia.Base.UnitTests.Collections
{
public class AvaloniaListExtenionsTests
{
#pragma warning disable CS0618 // Type or member is obsolete
[Fact]
public void CreateDerivedList_Creates_Initial_Items()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Add()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Add(4);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Insert()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Insert(1, 4);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Remove()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Remove(2);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_RemoveRange()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.RemoveRange(1, 2);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Move()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Move(2, 0);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Theory]
[InlineData(0, 2, 3)]
[InlineData(0, 2, 4)]
[InlineData(0, 2, 5)]
[InlineData(0, 4, 4)]
[InlineData(1, 2, 0)]
[InlineData(1, 2, 4)]
[InlineData(1, 2, 5)]
[InlineData(1, 4, 0)]
[InlineData(2, 2, 0)]
[InlineData(2, 2, 1)]
[InlineData(2, 2, 3)]
[InlineData(2, 2, 4)]
[InlineData(2, 2, 5)]
[InlineData(4, 2, 0)]
[InlineData(4, 2, 1)]
[InlineData(4, 2, 3)]
[InlineData(5, 1, 0)]
[InlineData(5, 1, 3)]
public void CreateDerivedList_Handles_MoveRange(int oldIndex, int count, int newIndex)
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3, 4, 5 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.MoveRange(oldIndex, count, newIndex);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Replace()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source[1] = 4;
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Clear()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Clear();
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
#pragma warning restore CS0618 // Type or member is obsolete
private class Wrapper
{
public Wrapper(int value)
{
Value = value;
}
public int Value { get; }
}
}
}

70
tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs

@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Utilities;
using Xunit;
namespace Avalonia.Base.UnitTests
{
public class WeakSubscriptionManagerTests
{
class EventSource
{
public event EventHandler<EventArgs> Event;
public void Fire()
{
Event?.Invoke(this, new EventArgs());
}
}
class Subscriber : IWeakSubscriber<EventArgs>
{
private readonly Action _onEvent;
public Subscriber(Action onEvent)
{
_onEvent = onEvent;
}
public void OnEvent(object sender, EventArgs ev)
{
_onEvent?.Invoke();
}
}
[Fact]
public void EventShoudBePassedToSubscriber()
{
bool handled = false;
var subscriber = new Subscriber(() => handled = true);
var source = new EventSource();
WeakSubscriptionManager.Subscribe(source, "Event", subscriber);
source.Fire();
Assert.True(handled);
}
[Fact]
public void EventHandlerShouldNotBeKeptAlive()
{
bool handled = false;
var source = new EventSource();
AddSubscriber(source, "Event", () => handled = true);
for (int c = 0; c < 10; c++)
{
GC.Collect();
GC.Collect(3, GCCollectionMode.Forced, true);
}
source.Fire();
Assert.False(handled);
}
private void AddSubscriber(EventSource source, string name, Action func)
{
WeakSubscriptionManager.Subscribe(source, name, new Subscriber(func));
}
}
}

2
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -102,9 +102,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
[InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)]
public void WriteableBitmapShouldBeUsable(PixelFormat fmt)
{
#pragma warning disable CS0618 // Type or member is obsolete
var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt);
#pragma warning restore CS0618 // Type or member is obsolete
var data = new int[256 * 256];
for (int y = 0; y < 256; y++)

Loading…
Cancel
Save