diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index 85e7a1f34d..620ec58ff3 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index c3b136d41d..1911c02677 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index d54cffba08..cc573825cd 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml index 5251a2fa55..c778b31c42 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml +++ b/samples/ControlCatalog/DecoratedWindow.xaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.DecoratedWindow" Title="Avalonia Control Gallery" - xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window"> + xmlns:local="clr-namespace:ControlCatalog" SystemDecorations="None" Name="Window"> diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 1e4bf2de38..d5513904c0 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -18,15 +18,15 @@ - - + + + Click="OnCloseClicked" /> diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 0e7351feb4..1f4d1e6018 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -54,11 +54,11 @@ Minimum: + NumberFormat="{Binding #upDown.NumberFormat}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/> Maximum: + NumberFormat="{Binding #upDown.NumberFormat}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/> Increment: - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - + + + + - - \ No newline at end of file + + diff --git a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs index 42f75a27e1..de9149e9a1 100644 --- a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs +++ b/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(); }); } diff --git a/src/Avalonia.Base/Animation/Animation.cs b/src/Avalonia.Base/Animation/Animation.cs index 03b2d17e44..6bb06367de 100644 --- a/src/Avalonia.Base/Animation/Animation.cs +++ b/src/Avalonia.Base/Animation/Animation.cs @@ -172,23 +172,6 @@ namespace Avalonia.Animation set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); } } - /// - /// Obsolete: Do not use this property, use instead. - /// - /// - [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); - } - } - /// /// Gets the children of the . /// diff --git a/src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs index b87b2681d6..9256c7be5e 100644 --- a/src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs @@ -37,17 +37,4 @@ namespace Avalonia.Animation.Animators } } - [Obsolete("Use ISolidColorBrushAnimator instead")] - public class SolidColorBrushAnimator : Animator - { - 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)); - } - } } diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 134e3b2ac7..2d7bab6cd6 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -468,41 +468,6 @@ namespace Avalonia }); } - /// - /// Subscribes to a property changed notifications for changes that originate from a - /// . - /// - /// The type of the property change sender. - /// The property changed observable. - /// Given a TTarget, returns the handler. - /// A disposable that can be used to terminate the subscription. - [Obsolete("Use overload taking Action.")] - public static IDisposable AddClassHandler( - this IObservable observable, - Func> handler) - where TTarget : class - { - return observable.Subscribe(e => SubscribeAdapter(e, handler)); - } - - /// - /// Observer method for . - /// - /// The sender type to accept. - /// The event args. - /// Given a TTarget, returns the handler. - private static void SubscribeAdapter( - AvaloniaPropertyChangedEventArgs e, - Func> handler) - where TTarget : class - { - if (e.Sender is TTarget target) - { - handler(target)(e); - } - } - private class BindingAdaptor : IBinding { private IObservable _source; diff --git a/src/Avalonia.Base/AvaloniaProperty`1.cs b/src/Avalonia.Base/AvaloniaProperty`1.cs index 3937ee5658..14acc2c47f 100644 --- a/src/Avalonia.Base/AvaloniaProperty`1.cs +++ b/src/Avalonia.Base/AvaloniaProperty`1.cs @@ -30,21 +30,6 @@ namespace Avalonia _changed = new Subject>(); } - /// - /// Initializes a new instance of the class. - /// - /// The property to copy. - /// The new owner type. - /// Optional overridden metadata. - [Obsolete("Use constructor with AvaloniaProperty instead.", true)] - protected AvaloniaProperty( - AvaloniaProperty source, - Type ownerType, - AvaloniaPropertyMetadata? metadata) - : this(source as AvaloniaProperty ?? throw new InvalidOperationException(), ownerType, metadata) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs index fb70729cf8..c4684960d6 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs +++ b/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 CreateDerivedList( - this IAvaloniaReadOnlyList collection, - Func select) - { - var result = new AvaloniaList(); - - collection.ForEachItem( - (i, item) => result.Insert(i, select(item)), - (i, item) => result.RemoveAt(i), - () => result.Clear()); - - return result; - } - /// /// Listens for property changed events from all items in a collection. /// diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index efcb7dfecb..86e4bffaa8 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -29,21 +29,6 @@ namespace Avalonia { } - /// - /// Initializes a new instance of the class. - /// - /// The property to copy. - /// The new owner type. - /// Optional overridden metadata. - [Obsolete("Use constructor with DirectPropertyBase instead.", true)] - protected DirectPropertyBase( - AvaloniaProperty source, - Type ownerType, - AvaloniaPropertyMetadata metadata) - : this(source as DirectPropertyBase ?? throw new InvalidOperationException(), ownerType, metadata) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/src/Avalonia.Base/EnumExtensions.cs b/src/Avalonia.Base/EnumExtensions.cs index 19eb42a700..9b74266b09 100644 --- a/src/Avalonia.Base/EnumExtensions.cs +++ b/src/Avalonia.Base/EnumExtensions.cs @@ -8,10 +8,6 @@ namespace Avalonia /// public static class EnumExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("This method is obsolete. Use HasAllFlags instead.")] - public static bool HasFlagCustom(this T value, T flag) where T : unmanaged, Enum - => value.HasAllFlags(flag); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe bool HasAllFlags(this T value, T flags) where T : unmanaged, Enum diff --git a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs index a95d66346a..2ebf01bcf6 100644 --- a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs +++ b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs @@ -158,6 +158,8 @@ namespace Avalonia.Input ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers); } } + + _lastPointer = (pointer, root.PointToScreen(position)); } private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element, @@ -195,7 +197,6 @@ namespace Avalonia.Input } el = root.PointerOverElement = element; - _lastPointer = (pointer, root.PointToScreen(position)); e.RoutedEvent = InputElement.PointerEnteredEvent; diff --git a/src/Avalonia.Base/Interactivity/RoutedEvent.cs b/src/Avalonia.Base/Interactivity/RoutedEvent.cs index a9b7dc8c89..37edb24cc1 100644 --- a/src/Avalonia.Base/Interactivity/RoutedEvent.cs +++ b/src/Avalonia.Base/Interactivity/RoutedEvent.cs @@ -113,24 +113,6 @@ namespace Avalonia.Interactivity { } - [Obsolete("Use overload taking Action.")] - public IDisposable AddClassHandler( - Func> 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( Action handler, RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, diff --git a/src/Avalonia.Base/Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs index 143ce13a1b..88d95c81fd 100644 --- a/src/Avalonia.Base/Layout/ILayoutManager.cs +++ b/src/Avalonia.Base/Layout/ILayoutManager.cs @@ -44,17 +44,6 @@ namespace Avalonia.Layout /// void ExecuteInitialLayoutPass(); - /// - /// Executes the initial layout pass on a layout root. - /// - /// The control to lay out. - /// - /// 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. - /// - [Obsolete("Call ExecuteInitialLayoutPass without parameter")] - void ExecuteInitialLayoutPass(ILayoutRoot root); - /// /// Registers a control as wanting to receive effective viewport notifications. /// diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs index b9ca6bfbd7..826947c39a 100644 --- a/src/Avalonia.Base/Layout/LayoutManager.cs +++ b/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; diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs index 101e867d56..527b63292d 100644 --- a/src/Avalonia.Base/Layout/Layoutable.cs +++ b/src/Avalonia.Base/Layout/Layoutable.cs @@ -460,20 +460,6 @@ namespace Avalonia.Layout _effectiveViewportChanged?.Invoke(this, e); } - /// - /// Marks a property as affecting the control's measurement. - /// - /// The properties. - /// - /// After a call to this method in a control's static constructor, any change to the - /// property will cause to be called on the element. - /// - [Obsolete("Use AffectsMeasure and specify the control type.")] - protected static void AffectsMeasure(params AvaloniaProperty[] properties) - { - AffectsMeasure(properties); - } - /// /// Marks a property as affecting the control's measurement. /// @@ -497,20 +483,6 @@ namespace Avalonia.Layout } } - /// - /// Marks a property as affecting the control's arrangement. - /// - /// The properties. - /// - /// After a call to this method in a control's static constructor, any change to the - /// property will cause to be called on the element. - /// - [Obsolete("Use AffectsArrange and specify the control type.")] - protected static void AffectsArrange(params AvaloniaProperty[] properties) - { - AffectsArrange(properties); - } - /// /// Marks a property as affecting the control's arrangement. /// diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs index 5f1617d778..cf8a31c3e9 100644 --- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs @@ -89,22 +89,6 @@ namespace Avalonia.Media.Imaging PlatformImpl.Dispose(); } - /// - /// Initializes a new instance of the class. - /// - /// The pixel format. - /// The pointer to the source bytes. - /// The size of the bitmap in device pixels. - /// The DPI of the bitmap. - /// The number of bytes per row. - [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)); - } - /// /// Initializes a new instance of the class. /// diff --git a/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs index 1f39b1344d..1aac8efac7 100644 --- a/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs @@ -9,18 +9,6 @@ namespace Avalonia.Media.Imaging /// public class WriteableBitmap : Bitmap { - /// - /// Initializes a new instance of the class. - /// - /// The size of the bitmap in device pixels. - /// The DPI of the bitmap. - /// The pixel format (optional). - /// An . - [Obsolete("Use overload taking an AlphaFormat.")] - public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) - : base(CreatePlatformImpl(size, dpi, format, null)) - { - } /// /// Initializes a new instance of the class. @@ -30,7 +18,7 @@ namespace Avalonia.Media.Imaging /// The pixel format (optional). /// The alpha format (optional). /// An . - 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)) { } diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs index d381979c1e..3c48c3469e 100644 --- a/src/Avalonia.Base/Utilities/MathUtilities.cs +++ b/src/Avalonia.Base/Utilities/MathUtilities.cs @@ -324,6 +324,41 @@ namespace Avalonia.Utilities return angle * 2 * Math.PI; } + /// + /// Calculates the point of an angle on an ellipse. + /// + /// The centre point of the ellipse. + /// The x radius of the ellipse. + /// The y radius of the ellipse. + /// The angle in radians. + /// A point on the ellipse. + 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); + } + + /// + /// Gets the minimum and maximum from the specified numbers. + /// + /// The first number. + /// The second number. + /// A tuple containing the minimum and maximum of the two specified numbers. + public static (double min, double max) GetMinMax(double a, double b) + { + return a < b ? (a, b) : (b, a); + } + + /// + /// Gets the minimum and maximum from the specified number and the difference with that number. + /// + /// The initial value to use. + /// The difference for . + /// A tuple containing the minimum and maximum of the specified number and the difference with that number. + public static (double min, double max) GetMinMaxFromDelta(double initialValue, double delta) + { + return GetMinMax(initialValue, initialValue + delta); + } + private static void ThrowCannotBeGreaterThanException(T min, T max) { throw new ArgumentException($"{min} cannot be greater than {max}."); diff --git a/src/Avalonia.Base/Utilities/WeakObservable.cs b/src/Avalonia.Base/Utilities/WeakObservable.cs index 6bf1d4082f..e1c350d539 100644 --- a/src/Avalonia.Base/Utilities/WeakObservable.cs +++ b/src/Avalonia.Base/Utilities/WeakObservable.cs @@ -9,31 +9,6 @@ namespace Avalonia.Utilities /// public static class WeakObservable { - /// - /// Converts a .NET event conforming to the standard .NET event pattern into an observable - /// sequence, subscribing weakly. - /// - /// The type of target. - /// The type of the event args. - /// Object instance that exposes the event to convert. - /// Name of the event to convert. - /// - [Obsolete("Use WeakEvent-based overload")] - public static IObservable> FromEventPattern( - TTarget target, - string eventName) - where TEventArgs : EventArgs - { - _ = target ?? throw new ArgumentNullException(nameof(target)); - _ = eventName ?? throw new ArgumentNullException(nameof(eventName)); - - return Observable.Create>(observer => - { - var handler = new Handler(observer); - WeakSubscriptionManager.Subscribe(target, eventName, handler); - return () => WeakSubscriptionManager.Unsubscribe(target, eventName, handler); - }).Publish().RefCount(); - } private class Handler : IWeakSubscriber, diff --git a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs deleted file mode 100644 index dc9e86cc32..0000000000 --- a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace Avalonia.Utilities -{ - /// - /// Manages subscriptions to events using weak listeners. - /// - public static class WeakSubscriptionManager - { - /// - /// Subscribes to an event on an object using a weak subscription. - /// - /// The type of the target. - /// The type of the event arguments. - /// The event source. - /// The name of the event. - /// The subscriber. - [Obsolete("Use WeakEvent")] - public static void Subscribe(TTarget target, string eventName, IWeakSubscriber subscriber) - where TEventArgs : EventArgs - { - _ = target ?? throw new ArgumentNullException(nameof(target)); - - var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); - - if (!dic.TryGetValue(eventName, out var sub)) - { - dic[eventName] = sub = new Subscription(dic, typeof(TTarget), target, eventName); - } - - sub.Add(new WeakReference>(subscriber)); - } - - /// - /// Unsubscribes from an event. - /// - /// The type of the event arguments. - /// The event source. - /// The name of the event. - /// The subscriber. - public static void Unsubscribe(object target, string eventName, IWeakSubscriber subscriber) - where T : EventArgs - { - if (SubscriptionTypeStorage.Subscribers.TryGetValue(target, out var dic)) - { - if (dic.TryGetValue(eventName, out var sub)) - { - sub.Remove(subscriber); - } - } - } - - private static class SubscriptionTypeStorage - where T : EventArgs - { - public static readonly ConditionalWeakTable> Subscribers - = new ConditionalWeakTable>(); - } - - private class SubscriptionDic : Dictionary> - where T : EventArgs - { - } - - private static readonly Dictionary> Accessors - = new Dictionary>(); - - private class Subscription where T : EventArgs - { - private readonly EventInfo _info; - private readonly SubscriptionDic _sdic; - private readonly object _target; - private readonly string _eventName; - private readonly Delegate _delegate; - - private WeakReference>?[] _data = new WeakReference>?[16]; - private int _count = 0; - - public Subscription(SubscriptionDic 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(); - - 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(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> s) - { - if (_count == _data.Length) - { - //Extend capacity - var ndata = new WeakReference>?[_data.Length*2]; - Array.Copy(_data, ndata, _data.Length); - _data = ndata; - } - _data[_count] = s!; - _count++; - } - - public void Remove(IWeakSubscriber s) - { - var removed = false; - - for (int c = 0; c < _count; ++c) - { - var reference = _data[c]; - IWeakSubscriber? 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(); - } - } - } -} diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 8feba116f0..69389def56 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -338,22 +338,6 @@ namespace Avalonia Contract.Requires(context != null); } - /// - /// Indicates that a property change should cause to be - /// called. - /// - /// The properties. - /// - /// 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. - /// - [Obsolete("Use AffectsRender and specify the control type.")] - protected static void AffectsRender(params AvaloniaProperty[] properties) - { - AffectsRender(properties); - } - /// /// Indicates that a property change should cause to be /// called. diff --git a/src/Avalonia.Base/VisualTree/IVisualTreeHost.cs b/src/Avalonia.Base/VisualTree/IVisualTreeHost.cs deleted file mode 100644 index e47377063f..0000000000 --- a/src/Avalonia.Base/VisualTree/IVisualTreeHost.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Avalonia.VisualTree -{ - /// - /// Interface for controls that host their own separate visual tree, such as popups. - /// - [Obsolete] - public interface IVisualTreeHost - { - /// - /// Gets the root of the hosted visual tree. - /// - /// - /// The root of the hosted visual tree. - /// - IVisual? Root { get; } - } -} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml index a5a94a2322..0a1a9e60c6 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml @@ -11,15 +11,20 @@ Stretch="Uniform" DestinationRect="0,0,8,8"> - - - - - - + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml index 1e507a91fe..f21b03cee2 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml @@ -11,15 +11,20 @@ Stretch="Uniform" DestinationRect="0,0,8,8"> - - - - - - + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index d42468f47e..cacd5c5a16 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -2167,7 +2167,23 @@ namespace Avalonia.Controls return desiredSize; } + + /// + protected override void OnDataContextBeginUpdate() + { + base.OnDataContextBeginUpdate(); + NotifyDataContextPropertyForAllRowCells(GetAllRows(), true); + } + + /// + protected override void OnDataContextEndUpdate() + { + base.OnDataContextEndUpdate(); + + NotifyDataContextPropertyForAllRowCells(GetAllRows(), false); + } + /// /// Raises the BeginningEdit event. /// @@ -3165,6 +3181,20 @@ namespace Avalonia.Controls } } + private static void NotifyDataContextPropertyForAllRowCells(IEnumerable 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; diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 083182a370..524362fcf9 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.Styling; @@ -104,7 +105,6 @@ namespace Avalonia.Controls private static readonly HashSet _loadedQueue = new HashSet(); private static readonly HashSet _loadedProcessingQueue = new HashSet(); - private bool _isAttachedToVisualTree = false; private bool _isLoaded = false; private DataTemplates? _dataTemplates; private IControl? _focusAdorner; @@ -347,7 +347,7 @@ namespace Avalonia.Controls internal void OnLoadedCore() { if (_isLoaded == false && - _isAttachedToVisualTree) + ((ILogical)this).IsAttachedToLogicalTree) { _isLoaded = true; OnLoaded(); @@ -395,7 +395,6 @@ namespace Avalonia.Controls protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTreeCore(e); - _isAttachedToVisualTree = true; InitializeIfNeeded(); @@ -406,7 +405,6 @@ namespace Avalonia.Controls protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTreeCore(e); - _isAttachedToVisualTree = false; OnUnloadedCore(); } diff --git a/src/Avalonia.Controls/DesktopApplicationExtensions.cs b/src/Avalonia.Controls/DesktopApplicationExtensions.cs index ddd4e57a40..ddaed6bb0d 100644 --- a/src/Avalonia.Controls/DesktopApplicationExtensions.cs +++ b/src/Avalonia.Controls/DesktopApplicationExtensions.cs @@ -8,8 +8,6 @@ namespace Avalonia.Controls { public static class DesktopApplicationExtensions { - [Obsolete("Running application without a cancellation token and a lifetime is no longer supported, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] - public static void Run(this Application app) => throw new NotSupportedException(); /// /// On desktop-style platforms runs the application's main loop until closable is closed diff --git a/src/Avalonia.Controls/DrawingPresenter.cs b/src/Avalonia.Controls/DrawingPresenter.cs deleted file mode 100644 index ee27aa7ec1..0000000000 --- a/src/Avalonia.Controls/DrawingPresenter.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using Avalonia.Controls.Shapes; -using Avalonia.Media; -using Avalonia.Metadata; - -namespace Avalonia.Controls -{ - [Obsolete("Use Image control with DrawingImage source")] - public class DrawingPresenter : Control - { - static DrawingPresenter() - { - AffectsMeasure(DrawingProperty); - AffectsRender(DrawingProperty); - } - - public static readonly StyledProperty DrawingProperty = - AvaloniaProperty.Register(nameof(Drawing)); - - public static readonly StyledProperty StretchProperty = - AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); - - [Content] - public Drawing Drawing - { - get => GetValue(DrawingProperty); - set => SetValue(DrawingProperty, value); - } - - public Stretch Stretch - { - get => GetValue(StretchProperty); - set => SetValue(StretchProperty, value); - } - - private Matrix _transform = Matrix.Identity; - - protected override Size MeasureOverride(Size availableSize) - { - if (Drawing == null) return new Size(); - - var (size, transform) = Shape.CalculateSizeAndTransform(availableSize, Drawing.GetBounds(), Stretch); - - _transform = transform; - - return size; - } - - public override void Render(DrawingContext context) - { - if (Drawing != null) - { - using (context.PushPreTransform(_transform)) - using (context.PushClip(new Rect(Bounds.Size))) - { - Drawing.Draw(context); - } - } - } - } -} diff --git a/src/Avalonia.Controls/LoggingExtensions.cs b/src/Avalonia.Controls/LoggingExtensions.cs index 9eb3b140f6..ef14d0b477 100644 --- a/src/Avalonia.Controls/LoggingExtensions.cs +++ b/src/Avalonia.Controls/LoggingExtensions.cs @@ -1,21 +1,10 @@ -using System; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Logging; namespace Avalonia { public static class LoggingExtensions { - [Obsolete("Use LogToTrace")] - public static T LogToDebug( - this T builder, - LogEventLevel level = LogEventLevel.Warning, - params string[] areas) - where T : AppBuilderBase, new() - { - return LogToTrace(builder, level, areas); - } - /// /// Logs Avalonia events to the sink. /// diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs index 265e7119eb..d22fdb2f84 100644 --- a/src/Avalonia.Controls/NativeMenuItem.cs +++ b/src/Avalonia.Controls/NativeMenuItem.cs @@ -186,13 +186,6 @@ namespace Avalonia.Controls /// public event EventHandler? Click; - [Obsolete("Use Click event.")] - public event EventHandler Clicked - { - add => Click += value; - remove => Click -= value; - } - void INativeMenuItemExporterEventsImplBridge.RaiseClicked() { Click?.Invoke(this, new EventArgs()); diff --git a/src/Avalonia.Controls/NativeMenuItemSeparator.cs b/src/Avalonia.Controls/NativeMenuItemSeparator.cs index 49b36e714d..97278130b1 100644 --- a/src/Avalonia.Controls/NativeMenuItemSeparator.cs +++ b/src/Avalonia.Controls/NativeMenuItemSeparator.cs @@ -1,16 +1,7 @@ -using System; - -namespace Avalonia.Controls +namespace Avalonia.Controls { - - [Obsolete("This class exists to maintain backwards compatibility with existing code. Use NativeMenuItemSeparator instead")] - public class NativeMenuItemSeperator : NativeMenuItemSeparator - { - } - public class NativeMenuItemSeparator : NativeMenuItemBase { - [Obsolete("This is a temporary hack to make our MenuItem recognize this as a separator, don't use", true)] - public string Header => "-"; + } } diff --git a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs index 2449f4c15c..630276767a 100644 --- a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs @@ -102,15 +102,12 @@ namespace Avalonia.Controls.Notifications Content = content }; - if (notification != null) + notificationControl.NotificationClosed += (sender, args) => { - notificationControl.NotificationClosed += (sender, args) => - { - notification.OnClose?.Invoke(); + notification?.OnClose?.Invoke(); - _items?.Remove(sender); - }; - } + _items?.Remove(sender); + }; notificationControl.PointerPressed += (sender, args) => { diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index 705e68e3ea..dc2b2cd7cc 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -45,14 +45,6 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterDirect(nameof(ClipValueToMinMax), updown => updown.ClipValueToMinMax, (updown, b) => updown.ClipValueToMinMax = b); - /// - /// Defines the property. - /// - [Obsolete] - public static readonly DirectProperty CultureInfoProperty = - AvaloniaProperty.RegisterDirect(nameof(CultureInfo), o => o.CultureInfo, - (o, v) => o.CultureInfo = v, CultureInfo.CurrentCulture); - /// /// Defines the property. /// @@ -187,21 +179,6 @@ namespace Avalonia.Controls set { SetAndRaise(ClipValueToMinMaxProperty, ref _clipValueToMinMax, value); } } - /// - /// Gets or sets the current CultureInfo. - /// - [Obsolete("CultureInfo is obsolete, please use NumberFormat instead.")] - public CultureInfo? CultureInfo - { - get { return _cultureInfo; } - set - { - SetAndRaise(CultureInfoProperty, ref _cultureInfo, value); - //Set and Raise the NumberFormatProperty when CultureInfo is changed. - SetAndRaise(NumberFormatProperty, ref _numberFormat, value?.NumberFormat); - } - } - /// /// Gets or sets the current NumberFormatInfo /// @@ -335,9 +312,6 @@ namespace Avalonia.Controls /// static NumericUpDown() { -#pragma warning disable CS0612 // Type or member is obsolete - CultureInfoProperty.Changed.Subscribe(OnCultureInfoChanged); -#pragma warning restore CS0612 // Type or member is obsolete NumberFormatProperty.Changed.Subscribe(OnNumberFormatChanged); FormatStringProperty.Changed.Subscribe(FormatStringChanged); IncrementProperty.Changed.Subscribe(IncrementChanged); @@ -416,19 +390,6 @@ namespace Avalonia.Controls } } - /// - /// Called when the property value changed. - /// - /// The old value. - /// The new value. - protected virtual void OnCultureInfoChanged(CultureInfo? oldValue, CultureInfo? newValue) - { - if (IsInitialized) - { - SyncTextAndValueProperties(false, null); - } - } - /// /// Called when the property value changed. /// @@ -729,20 +690,6 @@ namespace Avalonia.Controls } } - /// - /// Called when the property value changed. - /// - /// The event args. - private static void OnCultureInfoChanged(AvaloniaPropertyChangedEventArgs e) - { - if (e.Sender is NumericUpDown upDown) - { - var oldValue = (CultureInfo?)e.OldValue; - var newValue = (CultureInfo?)e.NewValue; - upDown.OnCultureInfoChanged(oldValue, newValue); - } - } - /// /// Called when the property value changed. /// diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs index 630d2d8efb..e1f6db9c60 100644 --- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs +++ b/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); }); } diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 3573ad9aaa..f71a0c6f48 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -22,9 +22,7 @@ namespace Avalonia.Controls.Primitives /// /// Displays a popup window. /// -#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 WindowManagerAddShadowHintProperty = AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); @@ -90,14 +88,6 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty PlacementTargetProperty = AvaloniaProperty.Register(nameof(PlacementTarget)); -#pragma warning disable 618 - /// - /// Defines the property. - /// - public static readonly StyledProperty ObeyScreenEdgesProperty = - AvaloniaProperty.Register(nameof(ObeyScreenEdges), true); -#pragma warning restore 618 - public static readonly StyledProperty OverlayDismissEventPassThroughProperty = AvaloniaProperty.Register(nameof(OverlayDismissEventPassThrough)); @@ -125,17 +115,6 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty VerticalOffsetProperty = AvaloniaProperty.Register(nameof(VerticalOffset)); - /// - /// Defines the property. - /// - [Obsolete("Use IsLightDismissEnabledProperty")] - public static readonly DirectProperty StaysOpenProperty = - AvaloniaProperty.RegisterDirect( - nameof(StaysOpen), - o => o.StaysOpen, - (o, v) => o.StaysOpen = v, - true); - /// /// Defines the property. /// @@ -301,13 +280,6 @@ namespace Avalonia.Controls.Primitives set { SetValue(PlacementTargetProperty, value); } } - [Obsolete("This property has no effect")] - public bool ObeyScreenEdges - { - get => GetValue(ObeyScreenEdgesProperty); - set => SetValue(ObeyScreenEdgesProperty, value); - } - /// /// Gets or sets a value indicating whether the event that closes the popup is passed /// through to the parent window. @@ -352,17 +324,6 @@ namespace Avalonia.Controls.Primitives set { SetValue(VerticalOffsetProperty, value); } } - /// - /// Gets or sets a value indicating whether the popup should stay open when the popup is - /// pressed or loses focus. - /// - [Obsolete("Use IsLightDismissEnabled")] - public bool StaysOpen - { - get => !IsLightDismissEnabled; - set => IsLightDismissEnabled = !value; - } - /// /// Gets or sets whether this popup appears on top of all other windows /// @@ -372,11 +333,6 @@ namespace Avalonia.Controls.Primitives set { SetValue(TopmostProperty, value); } } - /// - /// Gets the root of the popup window. - /// - IVisual? IVisualTreeHost.Root => _openState?.PopupHost.HostedVisualTreeRoot; - IPopupHost? IPopupHostProvider.PopupHost => Host; event Action? IPopupHostProvider.PopupHostChanged diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index dc52cc3ae2..2441da8920 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -297,9 +297,6 @@ namespace Avalonia.Controls.Primitives var e = new TemplateAppliedEventArgs(nameScope); OnApplyTemplate(e); -#pragma warning disable CS0618 // Type or member is obsolete - OnTemplateApplied(e); -#pragma warning restore CS0618 // Type or member is obsolete RaiseEvent(e); } @@ -378,15 +375,6 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Called when the control's template is applied. - /// - /// The event args. - [Obsolete("Use OnApplyTemplate")] - protected virtual void OnTemplateApplied(TemplateAppliedEventArgs e) - { - } - /// /// Called when the property changes. /// diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index 91114628ee..1e406157d7 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -251,11 +251,9 @@ namespace Avalonia.Controls TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166% -#pragma warning disable CS0618 // Type or member is obsolete // Remove these properties when we switch to fluent as default and removed the old one. IndeterminateStartingOffset = -dim; IndeterminateEndingOffset = dim; -#pragma warning restore CS0618 // Type or member is obsolete var padding = Padding; var rectangle = new RectangleGeometry( diff --git a/src/Avalonia.Controls/Remote/RemoteWidget.cs b/src/Avalonia.Controls/Remote/RemoteWidget.cs index a88e0fd3d8..27578ddc78 100644 --- a/src/Avalonia.Controls/Remote/RemoteWidget.cs +++ b/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()) { diff --git a/src/Avalonia.Controls/RichTextBlock.cs b/src/Avalonia.Controls/RichTextBlock.cs index 1f8abbc30d..3c902fa16a 100644 --- a/src/Avalonia.Controls/RichTextBlock.cs +++ b/src/Avalonia.Controls/RichTextBlock.cs @@ -381,9 +381,7 @@ namespace Avalonia.Controls var hit = TextLayout.HitTestPoint(point); var index = hit.TextPosition; -#pragma warning disable CS0618 // Type or member is obsolete switch (e.ClickCount) -#pragma warning restore CS0618 // Type or member is obsolete { case 1: if (clickToSelect) diff --git a/src/Avalonia.Controls/Shapes/Arc.cs b/src/Avalonia.Controls/Shapes/Arc.cs index 5ebb321f9b..de3814f215 100644 --- a/src/Avalonia.Controls/Shapes/Arc.cs +++ b/src/Avalonia.Controls/Shapes/Arc.cs @@ -1,8 +1,12 @@ using System; using Avalonia.Media; +using Avalonia.Utilities; namespace Avalonia.Controls.Shapes { + /// + /// Represents a circular or elliptical arc (a segment of a curve). + /// public class Arc : Shape { /// @@ -19,8 +23,12 @@ namespace Avalonia.Controls.Shapes static Arc() { - StrokeThicknessProperty.OverrideDefaultValue(1); - AffectsGeometry(BoundsProperty, StrokeThicknessProperty, StartAngleProperty, SweepAngleProperty); + StrokeThicknessProperty.OverrideDefaultValue(1.0d); + AffectsGeometry( + BoundsProperty, + StrokeThicknessProperty, + StartAngleProperty, + SweepAngleProperty); } /// @@ -42,10 +50,11 @@ namespace Avalonia.Controls.Shapes set => SetValue(SweepAngleProperty, value); } + /// 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); } } diff --git a/src/Avalonia.Controls/Shapes/Sector.cs b/src/Avalonia.Controls/Shapes/Sector.cs new file mode 100644 index 0000000000..a9b7e8939b --- /dev/null +++ b/src/Avalonia.Controls/Shapes/Sector.cs @@ -0,0 +1,97 @@ +using System; +using Avalonia.Media; +using Avalonia.Utilities; + +namespace Avalonia.Controls.Shapes +{ + /// + /// Represents a circular or elliptical sector (a pie-shaped closed region of a circle or ellipse). + /// + public class Sector : Shape + { + /// + /// Defines the property. + /// + public static readonly StyledProperty StartAngleProperty = + AvaloniaProperty.Register(nameof(StartAngle), 0.0d); + + /// + /// Defines the property. + /// + public static readonly StyledProperty SweepAngleProperty = + AvaloniaProperty.Register(nameof(SweepAngle), 0.0d); + + /// + /// Gets or sets the angle at which the sector's arc starts, in degrees. + /// + public double StartAngle + { + get => GetValue(StartAngleProperty); + set => SetValue(StartAngleProperty, value); + } + + /// + /// Gets or sets the angle, in degrees, added to the defining where the sector's arc ends. + /// A positive value is clockwise, negative is counter-clockwise. + /// + public double SweepAngle + { + get => GetValue(SweepAngleProperty); + set => SetValue(SweepAngleProperty, value); + } + + static Sector() + { + StrokeThicknessProperty.OverrideDefaultValue(1.0d); + AffectsGeometry( + BoundsProperty, + StrokeThicknessProperty, + StartAngleProperty, + SweepAngleProperty); + } + + /// + 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; + } + } +} diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index f3fb4d9a6d..ee41b94b5a 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -32,13 +32,6 @@ namespace Avalonia.Controls [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] public abstract class FileSystemDialog : SystemDialog { - [Obsolete("Use Directory")] - public string? InitialDirectory - { - get => Directory; - set => Directory = value; - } - /// /// Gets or sets the initial directory that will be displayed when the file system dialog /// is opened. @@ -87,7 +80,7 @@ namespace Avalonia.Controls DefaultExtension = DefaultExtension, FileTypeChoices = Filters?.Select(f => new FilePickerFileType(f.Name!) { Patterns = f.Extensions.Select(e => $"*.{e}").ToArray() }).ToArray(), Title = Title, - SuggestedStartLocation = InitialDirectory is { } directory + SuggestedStartLocation = Directory is { } directory ? new BclStorageFolder(new System.IO.DirectoryInfo(directory)) : null, ShowOverwritePrompt = ShowOverwritePrompt @@ -129,7 +122,7 @@ namespace Avalonia.Controls AllowMultiple = AllowMultiple, FileTypeFilter = Filters?.Select(f => new FilePickerFileType(f.Name!) { Patterns = f.Extensions.Select(e => $"*.{e}").ToArray() }).ToArray(), Title = Title, - SuggestedStartLocation = InitialDirectory is { } directory + SuggestedStartLocation = Directory is { } directory ? new BclStorageFolder(new System.IO.DirectoryInfo(directory)) : null }; @@ -142,13 +135,6 @@ namespace Avalonia.Controls [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] public class OpenFolderDialog : FileSystemDialog { - [Obsolete("Use Directory")] - public string? DefaultDirectory - { - get => Directory; - set => Directory = value; - } - /// /// Shows the open folder dialog. /// @@ -170,7 +156,7 @@ namespace Avalonia.Controls return new FolderPickerOpenOptions { Title = Title, - SuggestedStartLocation = InitialDirectory is { } directory + SuggestedStartLocation = Directory is { } directory ? new BclStorageFolder(new System.IO.DirectoryInfo(directory)) : null }; diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 4c9e9327d4..d53302ef22 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1165,9 +1165,7 @@ namespace Avalonia.Controls SetAndRaise(CaretIndexProperty, ref _caretIndex, index); -#pragma warning disable CS0618 // Type or member is obsolete switch (e.ClickCount) -#pragma warning restore CS0618 // Type or member is obsolete { case 1: if (clickToSelect) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 1c1df84431..47fc9d7988 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -404,9 +404,6 @@ namespace Avalonia.Controls LayoutManager?.Dispose(); } - [Obsolete("Use HandleResized(Size, PlatformResizeReason)")] - protected virtual void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified); - /// /// Handles a resize notification from . /// diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 9b4bb51aca..1f3b10c0cc 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -79,16 +79,6 @@ namespace Avalonia.Controls public static readonly StyledProperty SizeToContentProperty = AvaloniaProperty.Register(nameof(SizeToContent)); - /// - /// Enables or disables system window decorations (title bar, buttons, etc) - /// - [Obsolete("Use SystemDecorationsProperty instead")] - public static readonly DirectProperty HasSystemDecorationsProperty = - AvaloniaProperty.RegisterDirect( - nameof(HasSystemDecorations), - o => o.HasSystemDecorations, - (o, v) => o.HasSystemDecorations = v); - /// /// Defines the property. /// @@ -289,25 +279,6 @@ namespace Avalonia.Controls set { SetValue(TitleProperty, value); } } - /// - /// Enables or disables system window decorations (title bar, buttons, etc) - /// - [Obsolete("Use SystemDecorations instead")] - public bool HasSystemDecorations - { - get => SystemDecorations == SystemDecorations.Full; - set - { - var oldValue = HasSystemDecorations; - - if (oldValue != value) - { - SystemDecorations = value ? SystemDecorations.Full : SystemDecorations.None; - RaisePropertyChanged(HasSystemDecorationsProperty, oldValue, value); - } - } - } - /// /// Gets or sets if the ClientArea is Extended into the Window Decorations (chrome or border). /// @@ -985,9 +956,6 @@ namespace Avalonia.Controls Owner = null; } - [Obsolete("Use HandleResized(Size, PlatformResizeReason)")] - protected sealed override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified); - /// protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason) { @@ -1033,19 +1001,9 @@ namespace Avalonia.Controls base.OnPropertyChanged(change); if (change.Property == SystemDecorationsProperty) { - var (typedOldValue, typedNewValue) = change.GetOldAndNewValue(); + var (_, typedNewValue) = change.GetOldAndNewValue(); PlatformImpl?.SetSystemDecorations(typedNewValue); - - var o = typedOldValue == SystemDecorations.Full; - var n = typedNewValue == SystemDecorations.Full; - - if (o != n) - { -#pragma warning disable CS0618 // Type or member is obsolete - RaisePropertyChanged(HasSystemDecorationsProperty, o, n); -#pragma warning restore CS0618 // Type or member is obsolete - } } } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index cb68c1f6e1..89483cd566 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -94,9 +94,6 @@ namespace Avalonia.Controls public Screens Screens { get; private set; } - [Obsolete("No longer used. Always returns false.")] - protected bool AutoSizing => false; - /// /// Gets or sets the owner of the window. /// @@ -169,9 +166,6 @@ namespace Avalonia.Controls } } - [Obsolete("No longer used. Has no effect.")] - protected IDisposable BeginAutoSizing() => Disposable.Empty; - /// /// Ensures that the window is initialized. /// @@ -226,9 +220,6 @@ namespace Avalonia.Controls } } - [Obsolete("Use HandleResized(Size, PlatformResizeReason)")] - protected override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified); - /// /// Handles a resize notification from . /// diff --git a/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs b/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs index fb57058ae9..4153d2d38c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs @@ -3,6 +3,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Platform.Storage; +using Avalonia.Platform.Storage.FileIO; using Lifetimes = Avalonia.Controls.ApplicationLifetimes; namespace Avalonia.Diagnostics.Screenshots @@ -59,24 +61,25 @@ namespace Avalonia.Diagnostics.Screenshots protected async override Task GetStream(IControl control) { Stream? output = default; - var result = await new SaveFileDialog() + var result = await GetWindow(control).StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { + SuggestedStartLocation = new BclStorageFolder(new DirectoryInfo(ScreenshotsRoot)), Title = Title, - Filters = new() { new FileDialogFilter() { Name = "PNG", Extensions = new() { "png" } } }, - Directory = ScreenshotsRoot, - }.ShowAsync(GetWindow(control)); - if (!string.IsNullOrWhiteSpace(result)) + FileTypeChoices = new FilePickerFileType[] { new FilePickerFileType("PNG") { Patterns = new string[] { "*.png" } } } + }); + + if (result!=null && !string.IsNullOrWhiteSpace(result.Name)) { - var foldler = Path.GetDirectoryName(result); + var folder = Path.GetDirectoryName(result.Name); // Directory information for path, or null if path denotes a root directory or is // null. Returns System.String.Empty if path does not contain directory information. - if (!string.IsNullOrWhiteSpace(foldler)) + if (!string.IsNullOrWhiteSpace(folder)) { - if (!Directory.Exists(foldler)) + if (!Directory.Exists(folder)) { - Directory.CreateDirectory(foldler); + Directory.CreateDirectory(folder); } - output = new FileStream(result, FileMode.Create); + output = new FileStream(result.Name, FileMode.Create); } } return output; diff --git a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml index a1d06f800f..65df347ee2 100644 --- a/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml +++ b/src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml @@ -76,7 +76,11 @@ - + + + + + diff --git a/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs b/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs index e42a7b1a71..b233b46dd0 100644 --- a/src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs +++ b/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(); }); } diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs index 7b65cfb595..279e7e750d 100644 --- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs @@ -34,7 +34,7 @@ namespace Avalonia.OpenGL.Controls _attachment.Present(); } - context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds); + context.DrawImage(_bitmap, new Rect(_bitmap.Size), new Rect(Bounds.Size)); base.Render(context); } @@ -84,6 +84,7 @@ namespace Avalonia.OpenGL.Controls using (_context.MakeCurrent()) { var gl = _context.GlInterface; + gl.ActiveTexture(GL_TEXTURE0); gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.DeleteFramebuffer(_fb); diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs index 5c713804e9..9f69b4ee6e 100644 --- a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs +++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs @@ -2,6 +2,7 @@ using System; using System.Reactive.Linq; using Avalonia.VisualTree; using Avalonia.Controls; +using Avalonia.Interactivity; using ReactiveUI; namespace Avalonia.ReactiveUI @@ -25,27 +26,28 @@ namespace Avalonia.ReactiveUI public IObservable GetActivationForView(IActivatableView view) { if (!(view is IVisual visual)) return Observable.Return(false); - if (view is WindowBase window) return GetActivationForWindowBase(window); + if (view is Control control) return GetActivationForControl(control); return GetActivationForVisual(visual); } /// - /// Listens to Opened and Closed events for Avalonia windows. + /// Listens to Loaded and Unloaded + /// events for Avalonia Control. /// - private IObservable GetActivationForWindowBase(WindowBase window) + private IObservable GetActivationForControl(Control control) { - var windowLoaded = Observable - .FromEventPattern( - x => window.Opened += x, - x => window.Opened -= x) + var controlLoaded = Observable + .FromEventPattern( + x => control.Loaded += x, + x => control.Loaded -= x) .Select(args => true); - var windowUnloaded = Observable - .FromEventPattern( - x => window.Closed += x, - x => window.Closed -= x) + var controlUnloaded = Observable + .FromEventPattern( + x => control.Unloaded += x, + x => control.Unloaded -= x) .Select(args => false); - return windowLoaded - .Merge(windowUnloaded) + return controlLoaded + .Merge(controlUnloaded) .DistinctUntilChanged(); } diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs index 775014d419..2d848d4cd7 100644 --- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs +++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs @@ -50,7 +50,7 @@ namespace Avalonia.ReactiveUI /// ReactiveUI routing documentation website for more info. /// /// - public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger + public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger, IStyleable { /// /// for the property. @@ -126,6 +126,8 @@ namespace Avalonia.ReactiveUI /// public IViewLocator? ViewLocator { get; set; } + Type IStyleable.StyleKey => typeof(TransitioningContentControl); + /// /// Invoked when ReactiveUI router navigates to a view model. /// diff --git a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs index 869238b377..0750fef067 100644 --- a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs +++ b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs @@ -2,7 +2,7 @@ using System; using System.Reactive.Disposables; using Avalonia.Controls; - +using Avalonia.Styling; using ReactiveUI; using Splat; @@ -13,7 +13,7 @@ namespace Avalonia.ReactiveUI /// the ViewModel property and display it. This control is very useful /// inside a DataTemplate to display the View associated with a ViewModel. /// - public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger + public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger, IStyleable { /// /// for the property. @@ -78,6 +78,8 @@ namespace Avalonia.ReactiveUI /// public IViewLocator? ViewLocator { get; set; } + Type IStyleable.StyleKey => typeof(TransitioningContentControl); + /// /// Invoked when ReactiveUI router navigates to a view model. /// diff --git a/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml b/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml index 55f4893057..b8ade96b6f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml @@ -138,7 +138,11 @@ - + + + + + @@ -231,7 +235,11 @@ - + + + + + diff --git a/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml index 43fc8c7a34..724c317beb 100644 --- a/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml @@ -118,7 +118,7 @@ + IsLightDismissEnabled="False"> textures = stackalloc int[2]; fixed (int* ptex = textures) gl.GenTextures(2, ptex); _texture = textures[0]; @@ -139,7 +139,6 @@ namespace Avalonia.Skia gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); gl.BindTexture(GL_TEXTURE_2D, oldTexture); - } } } @@ -161,15 +160,15 @@ namespace Avalonia.Skia gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive); gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo); - gl.BindTexture(GL_TEXTURE_2D, _frontBuffer); gl.ActiveTexture(GL_TEXTURE0); + gl.BindTexture(GL_TEXTURE_2D, _frontBuffer); gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width, _bitmap.PixelSize.Height); gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo); - gl.BindTexture(GL_TEXTURE_2D, oldTexture); gl.ActiveTexture(oldActive); + gl.BindTexture(GL_TEXTURE_2D, oldTexture); gl.Finish(); } @@ -192,9 +191,8 @@ namespace Avalonia.Skia if(_disposed) return; _disposed = true; - var tex = new[] { _texture, _frontBuffer }; - fixed (int* ptex = tex) - gl.DeleteTextures(2, ptex); + var ptex = stackalloc[] { _texture, _frontBuffer }; + gl.DeleteTextures(2, ptex); } } diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs deleted file mode 100644 index b1b2d3d8f2..0000000000 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs +++ /dev/null @@ -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(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(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(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(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(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(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(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(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(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; } - } - } -} diff --git a/tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs b/tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs deleted file mode 100644 index 7b2cac2819..0000000000 --- a/tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs +++ /dev/null @@ -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 Event; - - public void Fire() - { - Event?.Invoke(this, new EventArgs()); - } - } - - class Subscriber : IWeakSubscriber - { - 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)); - } - } -} diff --git a/tests/Avalonia.Controls.UnitTests/LoadedTests.cs b/tests/Avalonia.Controls.UnitTests/LoadedTests.cs new file mode 100644 index 0000000000..aaf0dce30e --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/LoadedTests.cs @@ -0,0 +1,71 @@ +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.UnitTests; +using Moq; +using Xunit; + +namespace Avalonia.Controls.UnitTests; + +public class LoadedTests +{ + [Fact] + public void Window_Loads_And_Unloads() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + int loadedCount = 0, unloadedCount = 0; + var target = new Window(); + + target.Loaded += (_, _) => loadedCount++; + target.Unloaded += (_, _) => unloadedCount++; + + Assert.Equal(0, loadedCount); + Assert.Equal(0, unloadedCount); + + target.Show(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + Assert.True(target.IsLoaded); + + Assert.Equal(1, loadedCount); + Assert.Equal(0, unloadedCount); + + target.Close(); + + Assert.Equal(1, loadedCount); + Assert.Equal(1, unloadedCount); + Assert.False(target.IsLoaded); + } + } + + [Fact] + public void Control_Loads_And_Unloads() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + int loadedCount = 0, unloadedCount = 0; + var window = new Window(); + window.Show(); + + var target = new Button(); + + target.Loaded += (_, _) => loadedCount++; + target.Unloaded += (_, _) => unloadedCount++; + + Assert.Equal(0, loadedCount); + Assert.Equal(0, unloadedCount); + + window.Content = target; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + Assert.True(target.IsLoaded); + + Assert.Equal(1, loadedCount); + Assert.Equal(0, unloadedCount); + + window.Content = null; + + Assert.Equal(1, loadedCount); + Assert.Equal(1, unloadedCount); + Assert.False(target.IsLoaded); + } + } +} diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs index c10f0ffcdb..196375fb40 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -7,6 +7,7 @@ using System.Reactive; using System.Reactive.Subjects; using System.Reactive.Linq; using System.Collections.Generic; +using System.IO; using System.Runtime.Serialization; using System.Threading; using Avalonia.Controls.ApplicationLifetimes; @@ -17,6 +18,7 @@ using Avalonia.UnitTests; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; using Avalonia; +using Avalonia.Threading; using ReactiveUI; using DynamicData; using Xunit; @@ -93,13 +95,23 @@ namespace Avalonia.ReactiveUI.UnitTests var suspension = new AutoSuspendHelper(application.ApplicationLifetime); RxApp.SuspensionHost.CreateNewAppState = () => new AppState { Example = "Foo" }; RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true); - RxApp.SuspensionHost.SetupDefaultSuspendResume(new DummySuspensionDriver()); + RxApp.SuspensionHost.SetupDefaultSuspendResume(new FakeSuspensionDriver()); suspension.OnFrameworkInitializationCompleted(); lifetime.Shutdown(); + Assert.True(shouldPersistReceived); Assert.Equal("Foo", RxApp.SuspensionHost.GetAppState().Example); } } + + private class FakeSuspensionDriver : ISuspensionDriver + { + public IObservable LoadState() => Observable.Empty(); + + public IObservable SaveState(object state) => Observable.Empty(); + + public IObservable InvalidateState() => Observable.Empty(); + } } } diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index c66a3c4ba1..a4e669cb34 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -12,6 +12,7 @@ using Xunit; using Splat; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; +using Avalonia.Threading; namespace Avalonia.ReactiveUI.UnitTests { @@ -109,10 +110,12 @@ namespace Avalonia.ReactiveUI.UnitTests var fakeRenderedDecorator = new TestRoot(); fakeRenderedDecorator.Child = userControl; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.True(activated[0]); Assert.Equal(1, activated.Count); fakeRenderedDecorator.Child = null; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.True(activated[0]); Assert.False(activated[1]); Assert.Equal(2, activated.Count); @@ -139,9 +142,11 @@ namespace Avalonia.ReactiveUI.UnitTests var fakeRenderedDecorator = new TestRoot(); fakeRenderedDecorator.Child = userControl; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.True(userControl.Active); fakeRenderedDecorator.Child = null; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.False(userControl.Active); } @@ -154,9 +159,11 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.False(window.Active); window.Show(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.True(window.Active); window.Close(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.False(window.Active); } } @@ -171,9 +178,11 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.False(viewModel.IsActivated); window.Show(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.True(viewModel.IsActivated); window.Close(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.False(viewModel.IsActivated); } } @@ -187,9 +196,11 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.False(viewModel.IsActivated); root.Child = control; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.True(viewModel.IsActivated); root.Child = null; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.False(viewModel.IsActivated); } } diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs index 4dd60e21c5..4bf999bed0 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs @@ -1,5 +1,6 @@ using System.Reactive.Disposables; using Avalonia.Controls; +using Avalonia.Threading; using Avalonia.UnitTests; using ReactiveUI; using Splat; @@ -69,12 +70,14 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.False(view.ViewModel.IsActive); root.Child = view; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); Assert.True(view.ViewModel.IsActive); root.Child = null; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); @@ -90,12 +93,14 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.False(view.ViewModel.IsActive); root.Child = view; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); Assert.True(view.ViewModel.IsActive); root.Child = null; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs index 18a8a33f09..a71a500ed9 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs @@ -1,4 +1,5 @@ using System.Reactive.Disposables; +using Avalonia.Threading; using Avalonia.UnitTests; using ReactiveUI; using Splat; @@ -72,12 +73,14 @@ namespace Avalonia.ReactiveUI.UnitTests Assert.False(view.ViewModel.IsActive); view.Show(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); Assert.True(view.ViewModel.IsActive); view.Close(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); @@ -96,12 +99,14 @@ namespace Avalonia.ReactiveUI.UnitTests view.Show(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); Assert.True(view.ViewModel.IsActive); view.Close(); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(view.ViewModel); Assert.NotNull(view.DataContext); Assert.False(view.ViewModel.IsActive); diff --git a/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs index 244b5abc4e..56c7863861 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs @@ -15,6 +15,7 @@ using System.ComponentModel; using System.Threading.Tasks; using System.Reactive; using Avalonia.ReactiveUI; +using Avalonia.Threading; namespace Avalonia.ReactiveUI.UnitTests { @@ -75,6 +76,7 @@ namespace Avalonia.ReactiveUI.UnitTests Child = host }; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(host.Content); Assert.IsType(host.Content); Assert.Equal(defaultContent, host.Content); @@ -126,6 +128,7 @@ namespace Avalonia.ReactiveUI.UnitTests Child = host }; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(host.Content); Assert.IsType(host.Content); Assert.Equal(defaultContent, host.Content); @@ -191,6 +194,7 @@ namespace Avalonia.ReactiveUI.UnitTests Child = host }; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(host.Content); Assert.Equal(defaultContent, host.Content); diff --git a/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs index 858c476227..3d60e56c65 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using Avalonia.Threading; using Avalonia.UnitTests; using ReactiveUI; using Splat; @@ -46,6 +47,7 @@ namespace Avalonia.ReactiveUI.UnitTests Child = host }; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(host.Content); Assert.Equal(typeof(TextBlock), host.Content.GetType()); Assert.Equal(defaultContent, host.Content); @@ -91,6 +93,7 @@ namespace Avalonia.ReactiveUI.UnitTests Child = host }; + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); Assert.NotNull(host.Content); Assert.Equal(typeof(TextBlock), host.Content.GetType()); Assert.Equal(defaultContent, host.Content); diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index b629304b77..d52539c371 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/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++)