diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 83db67fcee..87fd47df25 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] public partial class Resource { diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 7884f13ddb..e864ea2007 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -6,7 +6,7 @@ tools $(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL true - NU1605 + NU1605;CS8632 diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index 27b28f35fc..4416437479 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -86,14 +86,15 @@ namespace Avalonia.Diagnostics.ViewModels public void SelectControl(IControl control) { var node = default(TreeNode); + IControl? c = control; - while (node == null && control != null) + while (node == null && c != null) { - node = FindNode(control); + node = FindNode(c); if (node == null) { - control = control.GetVisualParent(); + c = c.GetVisualParent(); } } diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs index a3330b65ae..60175ae588 100644 --- a/src/Avalonia.Input/AccessKeyHandler.cs +++ b/src/Avalonia.Input/AccessKeyHandler.cs @@ -188,7 +188,7 @@ namespace Avalonia.Input // If the menu is open, only match controls in the menu's visual tree. if (menuIsOpen) { - matches = matches.Where(x => MainMenu.IsVisualAncestorOf(x)); + matches = matches.Where(x => x is not null && MainMenu!.IsVisualAncestorOf(x)); } var match = matches.FirstOrDefault(); diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index c2197ee64d..def0a26b27 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -675,7 +675,7 @@ namespace Avalonia.Input /// . /// /// The parent control. - private void UpdateIsEffectivelyEnabled(InputElement parent) + private void UpdateIsEffectivelyEnabled(InputElement? parent) { IsEffectivelyEnabled = IsEnabledCore && (parent?.IsEffectivelyEnabled ?? true); diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Input/Pointer.cs index 433b275ce4..3012f07f6a 100644 --- a/src/Avalonia.Input/Pointer.cs +++ b/src/Avalonia.Input/Pointer.cs @@ -54,7 +54,7 @@ namespace Avalonia.Input Captured.DetachedFromVisualTree += OnCaptureDetached; } - IInputElement GetNextCapture(IVisual parent) + IInputElement? GetNextCapture(IVisual parent) { return parent as IInputElement ?? parent.FindAncestorOfType(); } diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs index ba7a3868c7..a2c4b0313b 100644 --- a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs @@ -143,7 +143,7 @@ namespace Avalonia.Animation.Animators if (!match(firstKeyType)) continue; - animator = (IAnimator)Activator.CreateInstance(animatorType); + animator = (IAnimator?)Activator.CreateInstance(animatorType); if (animator != null) { animator.Property = Property; diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 1b2142f6c9..1d7bfd3748 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -11,10 +11,10 @@ namespace Avalonia.Animation.Animators /// public class TransformAnimator : Animator { - DoubleAnimator _doubleAnimator; + DoubleAnimator? _doubleAnimator; /// - public override IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable obsMatch, Action onComplete) + public override IDisposable? Apply(Animation animation, Animatable control, IClock clock, IObservable obsMatch, Action onComplete) { var ctrl = (Visual)control; diff --git a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs b/src/Avalonia.Visuals/Animation/CompositePageTransition.cs index 2deebd7792..2a6eae3e38 100644 --- a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs +++ b/src/Avalonia.Visuals/Animation/CompositePageTransition.cs @@ -37,7 +37,7 @@ namespace Avalonia.Animation public List PageTransitions { get; set; } = new List(); /// - public Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) + public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) { var transitionTasks = PageTransitions .Select(transition => transition.Start(from, to, forward, cancellationToken)) diff --git a/src/Avalonia.Visuals/Animation/CrossFade.cs b/src/Avalonia.Visuals/Animation/CrossFade.cs index 5eaa920b32..608a0880ec 100644 --- a/src/Avalonia.Visuals/Animation/CrossFade.cs +++ b/src/Avalonia.Visuals/Animation/CrossFade.cs @@ -100,7 +100,7 @@ namespace Avalonia.Animation } /// - public async Task Start(Visual from, Visual to, CancellationToken cancellationToken) + public async Task Start(Visual? from, Visual? to, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -112,7 +112,7 @@ namespace Avalonia.Animation { if (to != null) { - disposables.Add(to.SetValue(Visual.OpacityProperty, 0, Data.BindingPriority.Animation)); + disposables.Add(to.SetValue(Visual.OpacityProperty, 0, Data.BindingPriority.Animation)!); } if (from != null) @@ -151,7 +151,7 @@ namespace Avalonia.Animation /// /// A that tracks the progress of the animation. /// - Task IPageTransition.Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) + Task IPageTransition.Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) { return Start(from, to, cancellationToken); } diff --git a/src/Avalonia.Visuals/Animation/IPageTransition.cs b/src/Avalonia.Visuals/Animation/IPageTransition.cs index 2d19ddbb5b..5abfdbbd0d 100644 --- a/src/Avalonia.Visuals/Animation/IPageTransition.cs +++ b/src/Avalonia.Visuals/Animation/IPageTransition.cs @@ -26,6 +26,6 @@ namespace Avalonia.Animation /// /// A that tracks the progress of the animation. /// - Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken); + Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken); } } diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs index 7d033ccf61..b5a6062593 100644 --- a/src/Avalonia.Visuals/Animation/PageSlide.cs +++ b/src/Avalonia.Visuals/Animation/PageSlide.cs @@ -62,7 +62,7 @@ namespace Avalonia.Animation public Easing SlideOutEasing { get; set; } = new LinearEasing(); /// - public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken) + public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -155,17 +155,17 @@ namespace Avalonia.Animation /// /// Any one of the parameters may be null, but not both. /// - private static IVisual GetVisualParent(IVisual from, IVisual to) + private static IVisual GetVisualParent(IVisual? from, IVisual? to) { - var p1 = (from ?? to).VisualParent; - var p2 = (to ?? from).VisualParent; + var p1 = (from ?? to)!.VisualParent; + var p2 = (to ?? from)!.VisualParent; if (p1 != null && p2 != null && p1 != p2) { throw new ArgumentException("Controls for PageSlide must have same parent."); } - return p1; + return p1 ?? throw new InvalidOperationException("Cannot determine visual parent."); } } } diff --git a/src/Avalonia.Visuals/Animation/RenderLoopClock.cs b/src/Avalonia.Visuals/Animation/RenderLoopClock.cs index 504caef461..942e7eb7c6 100644 --- a/src/Avalonia.Visuals/Animation/RenderLoopClock.cs +++ b/src/Avalonia.Visuals/Animation/RenderLoopClock.cs @@ -9,7 +9,9 @@ namespace Avalonia.Animation { protected override void Stop() { - AvaloniaLocator.Current.GetService().Remove(this); + var loop = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IRenderLoop."); + loop.Remove(this); } bool IRenderLoopTask.NeedsUpdate => HasSubscriptions; diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index ee4f70e074..ad9a5fd71a 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -5,6 +5,8 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. @@ -77,4 +79,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWr InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. -Total Issues: 78 +Total Issues: 79 diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index 51ebfcb11f..486f08913c 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -15,4 +15,5 @@ + diff --git a/src/Avalonia.Visuals/CornerRadius.cs b/src/Avalonia.Visuals/CornerRadius.cs index 037bb16e31..893f7c4565 100644 --- a/src/Avalonia.Visuals/CornerRadius.cs +++ b/src/Avalonia.Visuals/CornerRadius.cs @@ -91,7 +91,7 @@ namespace Avalonia /// /// The Object to compare against. /// True if the Object is equal to this corner radius; False otherwise. - public override bool Equals(object obj) => obj is CornerRadius other && Equals(other); + public override bool Equals(object? obj) => obj is CornerRadius other && Equals(other); public override int GetHashCode() { diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 243dafe817..b08a0eb98a 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -272,7 +272,7 @@ namespace Avalonia /// /// The Object to compare against. /// True if the Object is equal to this matrix; False otherwise. - public override bool Equals(object obj) => obj is Matrix other && Equals(other); + public override bool Equals(object? obj) => obj is Matrix other && Equals(other); /// /// Returns the hash code for this instance. diff --git a/src/Avalonia.Visuals/Media/BoxShadow.cs b/src/Avalonia.Visuals/Media/BoxShadow.cs index 50f75365b0..b01f59f5f8 100644 --- a/src/Avalonia.Visuals/Media/BoxShadow.cs +++ b/src/Avalonia.Visuals/Media/BoxShadow.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using Avalonia.Animation.Animators; @@ -26,7 +27,7 @@ namespace Avalonia.Media return OffsetX.Equals(other.OffsetX) && OffsetY.Equals(other.OffsetY) && Blur.Equals(other.Blur) && Spread.Equals(other.Spread) && Color.Equals(other.Color); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is BoxShadow other && Equals(other); } @@ -59,7 +60,7 @@ namespace Avalonia.Media _index = 0; } - public bool TryReadString(out string s) + public bool TryReadString([MaybeNullWhen(false)] out string s) { s = null; if (_index >= _arr.Length) @@ -152,11 +153,11 @@ namespace Avalonia.Media tokenizer.TryReadString(out var token5); if (token4 != null) - blur = double.Parse(token3, CultureInfo.InvariantCulture); + blur = double.Parse(token3!, CultureInfo.InvariantCulture); if (token5 != null) - spread = double.Parse(token4, CultureInfo.InvariantCulture); + spread = double.Parse(token4!, CultureInfo.InvariantCulture); - var color = Color.Parse(token5 ?? token4 ?? token3); + var color = Color.Parse(token5 ?? token4 ?? token3!); return new BoxShadow { IsInset = inset, diff --git a/src/Avalonia.Visuals/Media/BoxShadows.cs b/src/Avalonia.Visuals/Media/BoxShadows.cs index 810ac70b99..4614ea4e3c 100644 --- a/src/Avalonia.Visuals/Media/BoxShadows.cs +++ b/src/Avalonia.Visuals/Media/BoxShadows.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media public struct BoxShadows { private readonly BoxShadow _first; - private readonly BoxShadow[] _list; + private readonly BoxShadow[]? _list; public int Count { get; } static BoxShadows() @@ -39,7 +39,7 @@ namespace Avalonia.Media throw new IndexOutOfRangeException(); if (c == 0) return _first; - return _list[c - 1]; + return _list![c - 1]; } } @@ -134,7 +134,7 @@ namespace Avalonia.Media return true; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is BoxShadows other && Equals(other); } diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index cf7f5f531c..11fbe9393a 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media AvaloniaProperty.Register(nameof(Opacity), 1.0); /// - public event EventHandler Invalidated; + public event EventHandler? Invalidated; static Brush() { @@ -43,18 +43,20 @@ namespace Avalonia.Media /// The . public static IBrush Parse(string s) { - Contract.Requires(s != null); - Contract.Requires(s.Length > 0); + _ = s ?? throw new ArgumentNullException(nameof(s)); - if (s[0] == '#') + if (s.Length > 0) { - return new ImmutableSolidColorBrush(Color.Parse(s)); - } + if (s[0] == '#') + { + return new ImmutableSolidColorBrush(Color.Parse(s)); + } - var brush = KnownColors.GetKnownBrush(s); - if (brush != null) - { - return brush; + var brush = KnownColors.GetKnownBrush(s); + if (brush != null) + { + return brush; + } } throw new FormatException($"Invalid brush string: '{s}'."); diff --git a/src/Avalonia.Visuals/Media/BrushConverter.cs b/src/Avalonia.Visuals/Media/BrushConverter.cs index b3c105ffc7..c501835c15 100644 --- a/src/Avalonia.Visuals/Media/BrushConverter.cs +++ b/src/Avalonia.Visuals/Media/BrushConverter.cs @@ -9,14 +9,14 @@ namespace Avalonia.Media /// public class BrushConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { - return Brush.Parse((string)value); + return value is string s ? Brush.Parse(s) : null; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Media/BrushExtensions.cs b/src/Avalonia.Visuals/Media/BrushExtensions.cs index 87e698e705..2fc8778a5e 100644 --- a/src/Avalonia.Visuals/Media/BrushExtensions.cs +++ b/src/Avalonia.Visuals/Media/BrushExtensions.cs @@ -18,7 +18,7 @@ namespace Avalonia.Media /// public static IBrush ToImmutable(this IBrush brush) { - Contract.Requires(brush != null); + _ = brush ?? throw new ArgumentNullException(nameof(brush)); return (brush as IMutableBrush)?.ToImmutable() ?? brush; } @@ -33,7 +33,7 @@ namespace Avalonia.Media /// public static ImmutableDashStyle ToImmutable(this IDashStyle style) { - Contract.Requires(style != null); + _ = style ?? throw new ArgumentNullException(nameof(style)); return style as ImmutableDashStyle ?? ((DashStyle)style).ToImmutable(); } @@ -48,7 +48,7 @@ namespace Avalonia.Media /// public static ImmutablePen ToImmutable(this IPen pen) { - Contract.Requires(pen != null); + _ = pen ?? throw new ArgumentNullException(nameof(pen)); return pen as ImmutablePen ?? ((Pen)pen).ToImmutable(); } diff --git a/src/Avalonia.Visuals/Media/CharacterHit.cs b/src/Avalonia.Visuals/Media/CharacterHit.cs index f018b2d8a9..6bbbff4f5b 100644 --- a/src/Avalonia.Visuals/Media/CharacterHit.cs +++ b/src/Avalonia.Visuals/Media/CharacterHit.cs @@ -41,7 +41,7 @@ namespace Avalonia.Media return FirstCharacterIndex == other.FirstCharacterIndex && TrailingLength == other.TrailingLength; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is CharacterHit other && Equals(other); } diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index a57a962db4..083c15cd13 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -280,7 +280,7 @@ namespace Avalonia.Media return A == other.A && R == other.R && G == other.G && B == other.B; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Color other && Equals(other); } diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Visuals/Media/DashStyle.cs index acba2d3615..2ec9436ede 100644 --- a/src/Avalonia.Visuals/Media/DashStyle.cs +++ b/src/Avalonia.Visuals/Media/DashStyle.cs @@ -133,7 +133,7 @@ namespace Avalonia.Media } } - private void DashesChanged(object sender, NotifyCollectionChangedEventArgs e) + private void DashesChanged(object? sender, NotifyCollectionChangedEventArgs e) { Invalidated?.Invoke(this, e); } diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs index 8e8b116a04..2fbef8c89f 100644 --- a/src/Avalonia.Visuals/Media/DrawingContext.cs +++ b/src/Avalonia.Visuals/Media/DrawingContext.cs @@ -20,9 +20,9 @@ namespace Avalonia.Media private static ThreadSafeObjectPool> TransformStackPool { get; } = ThreadSafeObjectPool>.Default; - private Stack _states = StateStackPool.Get(); + private Stack? _states = StateStackPool.Get(); - private Stack _transformContainers = TransformStackPool.Get(); + private Stack? _transformContainers = TransformStackPool.Get(); readonly struct TransformContainer { @@ -80,7 +80,7 @@ namespace Avalonia.Media /// The rect in the output to draw to. public void DrawImage(IImage source, Rect rect) { - Contract.Requires(source != null); + _ = source ?? throw new ArgumentNullException(nameof(source)); DrawImage(source, new Rect(source.Size), rect); } @@ -94,7 +94,7 @@ namespace Avalonia.Media /// The bitmap interpolation mode. public void DrawImage(IImage source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default) { - Contract.Requires(source != null); + _ = source ?? throw new ArgumentNullException(nameof(source)); source.Draw(this, sourceRect, destRect, bitmapInterpolationMode); } @@ -121,7 +121,8 @@ namespace Avalonia.Media /// The geometry. public void DrawGeometry(IBrush brush, IPen pen, Geometry geometry) { - DrawGeometry(brush, pen, geometry.PlatformImpl); + if (geometry.PlatformImpl is not null) + DrawGeometry(brush, pen, geometry.PlatformImpl); } /// @@ -132,7 +133,7 @@ namespace Avalonia.Media /// The geometry. public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) { - Contract.Requires(geometry != null); + _ = geometry ?? throw new ArgumentNullException(nameof(geometry)); if (brush != null || PenIsVisible(pen)) { @@ -157,7 +158,7 @@ namespace Avalonia.Media /// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// - public void DrawRectangle(IBrush brush, IPen pen, Rect rect, double radiusX = 0, double radiusY = 0, + public void DrawRectangle(IBrush? brush, IPen? pen, Rect rect, double radiusX = 0, double radiusY = 0, BoxShadows boxShadows = default) { if (brush == null && !PenIsVisible(pen)) @@ -201,7 +202,7 @@ namespace Avalonia.Media /// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// - public void DrawEllipse(IBrush brush, IPen pen, Point center, double radiusX, double radiusY) + public void DrawEllipse(IBrush? brush, IPen? pen, Point center, double radiusX, double radiusY) { if (brush == null && !PenIsVisible(pen)) { @@ -230,7 +231,7 @@ namespace Avalonia.Media /// The text. public void DrawText(IBrush foreground, Point origin, FormattedText text) { - Contract.Requires(text != null); + _ = text ?? throw new ArgumentNullException(nameof(text)); if (foreground != null) { @@ -245,7 +246,7 @@ namespace Avalonia.Media /// The glyph run. public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun) { - Contract.Requires(glyphRun != null); + _ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun)); if (foreground != null) { @@ -279,11 +280,14 @@ namespace Avalonia.Media Clip, MatrixContainer, GeometryClip, - OpacityMask + OpacityMask, } public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix)) { + if (context._states is null) + throw new ObjectDisposedException(nameof(DrawingContext)); + _context = context; _type = type; _matrix = matrix; @@ -295,6 +299,8 @@ namespace Avalonia.Media { if (_type == PushedStateType.None) return; + if (_context._states is null || _context._transformContainers is null) + throw new ObjectDisposedException(nameof(DrawingContext)); if (_context._currentLevel != _level) throw new InvalidOperationException("Wrong Push/Pop state order"); _context._currentLevel--; @@ -343,7 +349,14 @@ namespace Avalonia.Media /// A disposable used to undo the clip geometry. public PushedState PushGeometryClip(Geometry clip) { - Contract.Requires(clip != null); + _ = clip ?? throw new ArgumentNullException(nameof(clip)); + + // HACK: This check was added when nullable annotations pointed out that we're potentially + // pushing a null value for the clip here. Ideally we'd return an empty PushedState here but + // I don't want to make that change as part of adding nullable annotations. + if (clip.PlatformImpl is null) + throw new InvalidOperationException("Cannot push empty geometry clip."); + PlatformImpl.PushGeometryClip(clip.PlatformImpl); return new PushedState(this, PushedState.PushedStateType.GeometryClip); } @@ -407,6 +420,8 @@ namespace Avalonia.Media /// A disposable used to undo the transformation. public PushedState PushTransformContainer() { + if (_transformContainers is null) + throw new ObjectDisposedException(nameof(DrawingContext)); _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform)); _currentContainerTransform = CurrentTransform * _currentContainerTransform; _currentTransform = Matrix.Identity; @@ -418,6 +433,8 @@ namespace Avalonia.Media /// public void Dispose() { + if (_states is null || _transformContainers is null) + throw new ObjectDisposedException(nameof(DrawingContext)); while (_states.Count != 0) _states.Peek().Dispose(); StateStackPool.Return(_states); @@ -430,7 +447,7 @@ namespace Avalonia.Media PlatformImpl.Dispose(); } - private static bool PenIsVisible(IPen pen) + private static bool PenIsVisible(IPen? pen) { return pen?.Brush != null && pen.Thickness > 0; } diff --git a/src/Avalonia.Visuals/Media/DrawingImage.cs b/src/Avalonia.Visuals/Media/DrawingImage.cs index 6fa8d397a5..488822b693 100644 --- a/src/Avalonia.Visuals/Media/DrawingImage.cs +++ b/src/Avalonia.Visuals/Media/DrawingImage.cs @@ -26,7 +26,7 @@ namespace Avalonia.Media AvaloniaProperty.Register(nameof(Drawing)); /// - public event EventHandler Invalidated; + public event EventHandler? Invalidated; /// /// Gets or sets the drawing content. diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs index bae6dc3d8c..7f9abaf531 100644 --- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs +++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Platform; namespace Avalonia.Media @@ -95,9 +96,10 @@ namespace Avalonia.Media } /// - protected override IGeometryImpl CreateDefiningGeometry() + protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); if (Rect != default) return factory.CreateEllipseGeometry(Rect); diff --git a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs b/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs index 6b699acd2e..cdea5f5fa3 100644 --- a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs @@ -78,7 +78,7 @@ namespace Avalonia.Media AvaloniaProperty.Register(nameof(FallbackColor)); /// - public event EventHandler Invalidated; + public event EventHandler? Invalidated; /// /// Gets or Sets the BackgroundSource . @@ -299,14 +299,14 @@ namespace Avalonia.Media var lightness = (max + min) / 2.0; - lightness = 1 - ((1 - lightness) * luminosityOpacity.Value); + lightness = 1 - ((1 - lightness) * (luminosityOpacity ?? 1)); lightness = 0.13 + (lightness * 0.74); var luminosityColor = new Color(255, Trim(lightness), Trim(lightness), Trim(lightness)); var compensationMultiplier = 1 - PlatformTransparencyCompensationLevel; - return new Color((byte)(255 * Math.Max(Math.Min(PlatformTransparencyCompensationLevel + (luminosityOpacity.Value * compensationMultiplier), 1.0), 0.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B); + return new Color((byte)(255 * Math.Max(Math.Min(PlatformTransparencyCompensationLevel + ((luminosityOpacity ?? 1) * compensationMultiplier), 1.0), 0.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B); } /// diff --git a/src/Avalonia.Visuals/Media/FontFallback.cs b/src/Avalonia.Visuals/Media/FontFallback.cs index 240604c5c1..8943b57b31 100644 --- a/src/Avalonia.Visuals/Media/FontFallback.cs +++ b/src/Avalonia.Visuals/Media/FontFallback.cs @@ -8,7 +8,7 @@ /// /// Get or set the fallback /// - public FontFamily FontFamily { get; set; } + public FontFamily FontFamily { get; set; } = FontFamily.Default; /// /// Get or set the that is covered by the fallback. diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Visuals/Media/FontFamily.cs index f018733235..c4ebc1e0d4 100644 --- a/src/Avalonia.Visuals/Media/FontFamily.cs +++ b/src/Avalonia.Visuals/Media/FontFamily.cs @@ -27,7 +27,7 @@ namespace Avalonia.Media /// Specifies the base uri that is used to resolve font family assets. /// The name of the . /// Base uri must be an absolute uri. - public FontFamily(Uri baseUri, string name) + public FontFamily(Uri? baseUri, string name) { if (string.IsNullOrEmpty(name)) { @@ -77,7 +77,7 @@ namespace Avalonia.Media /// The family key. /// /// Key is only used for custom fonts. - public FontFamilyKey Key { get; } + public FontFamilyKey? Key { get; } /// /// Returns True if this instance is the system's default. @@ -95,7 +95,7 @@ namespace Avalonia.Media private struct FontFamilyIdentifier { - public FontFamilyIdentifier(string name, Uri source) + public FontFamilyIdentifier(string name, Uri? source) { Name = name; Source = source; @@ -103,7 +103,7 @@ namespace Avalonia.Media public string Name { get; } - public Uri Source { get; } + public Uri? Source { get; } } private static FontFamilyIdentifier GetFontFamilyIdentifier(string name) @@ -152,7 +152,7 @@ namespace Avalonia.Media /// /// Specified family is not supported. /// - public static FontFamily Parse(string s, Uri baseUri) + public static FontFamily Parse(string s, Uri? baseUri) { if (string.IsNullOrEmpty(s)) { @@ -192,12 +192,12 @@ namespace Avalonia.Media } } - public static bool operator !=(FontFamily a, FontFamily b) + public static bool operator !=(FontFamily? a, FontFamily? b) { return !(a == b); } - public static bool operator ==(FontFamily a, FontFamily b) + public static bool operator ==(FontFamily? a, FontFamily? b) { if (ReferenceEquals(a, b)) { @@ -207,7 +207,7 @@ namespace Avalonia.Media return !(a is null) && a.Equals(b); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(this, obj)) { diff --git a/src/Avalonia.Visuals/Media/FontManager.cs b/src/Avalonia.Visuals/Media/FontManager.cs index 33e7bf0200..72c1c8dcac 100644 --- a/src/Avalonia.Visuals/Media/FontManager.cs +++ b/src/Avalonia.Visuals/Media/FontManager.cs @@ -16,7 +16,7 @@ namespace Avalonia.Media private readonly ConcurrentDictionary _glyphTypefaceCache = new ConcurrentDictionary(); private readonly FontFamily _defaultFontFamily; - private readonly IReadOnlyList _fontFallbacks; + private readonly IReadOnlyList? _fontFallbacks; public FontManager(IFontManagerImpl platformImpl) { @@ -105,7 +105,7 @@ namespace Avalonia.Media if (typeface.FontFamily == _defaultFontFamily) { - return null; + throw new InvalidOperationException($"Could not create glyph typeface for: {typeface.FontFamily.Name}."); } typeface = new Typeface(_defaultFontFamily, typeface.Style, typeface.Weight); @@ -126,7 +126,7 @@ namespace Avalonia.Media /// public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, - FontFamily fontFamily, CultureInfo culture, out Typeface typeface) + FontFamily? fontFamily, CultureInfo? culture, out Typeface typeface) { if(_fontFallbacks != null) { diff --git a/src/Avalonia.Visuals/Media/FontManagerOptions.cs b/src/Avalonia.Visuals/Media/FontManagerOptions.cs index 983fd6eb32..54227dce0f 100644 --- a/src/Avalonia.Visuals/Media/FontManagerOptions.cs +++ b/src/Avalonia.Visuals/Media/FontManagerOptions.cs @@ -4,8 +4,8 @@ namespace Avalonia.Media { public class FontManagerOptions { - public string DefaultFamilyName { get; set; } + public string? DefaultFamilyName { get; set; } - public IReadOnlyList FontFallbacks { get; set; } + public IReadOnlyList? FontFallbacks { get; set; } } } diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs index 96312a5466..99daaf2143 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs @@ -122,12 +122,12 @@ namespace Avalonia.Media.Fonts } } - public static bool operator !=(FamilyNameCollection a, FamilyNameCollection b) + public static bool operator !=(FamilyNameCollection? a, FamilyNameCollection? b) { return !(a == b); } - public static bool operator ==(FamilyNameCollection a, FamilyNameCollection b) + public static bool operator ==(FamilyNameCollection? a, FamilyNameCollection? b) { if (ReferenceEquals(a, b)) { @@ -144,7 +144,7 @@ namespace Avalonia.Media.Fonts /// /// true if the specified is equal to this instance; otherwise, false. /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (!(obj is FamilyNameCollection other)) { diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs index eee24e856d..f607c67fed 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs @@ -12,7 +12,7 @@ namespace Avalonia.Media.Fonts /// /// /// - public FontFamilyKey(Uri source, Uri baseUri = null) + public FontFamilyKey(Uri source, Uri? baseUri = null) { Source = source ?? throw new ArgumentNullException(nameof(source)); @@ -27,7 +27,7 @@ namespace Avalonia.Media.Fonts /// /// A base URI to use if is relative /// - public Uri BaseUri { get; } + public Uri? BaseUri { get; } /// /// Returns a hash code for this instance. @@ -55,12 +55,12 @@ namespace Avalonia.Media.Fonts } } - public static bool operator !=(FontFamilyKey a, FontFamilyKey b) + public static bool operator !=(FontFamilyKey? a, FontFamilyKey? b) { return !(a == b); } - public static bool operator ==(FontFamilyKey a, FontFamilyKey b) + public static bool operator ==(FontFamilyKey? a, FontFamilyKey? b) { if (ReferenceEquals(a, b)) { @@ -77,7 +77,7 @@ namespace Avalonia.Media.Fonts /// /// true if the specified is equal to this instance; otherwise, false. /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (!(obj is FontFamilyKey other)) { diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs index 02298417a4..203c8dc221 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs @@ -32,7 +32,8 @@ namespace Avalonia.Media.Fonts /// private static IEnumerable GetFontAssetsBySource(FontFamilyKey fontFamilyKey) { - var assetLoader = AvaloniaLocator.Current.GetService(); + var assetLoader = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IAssetLoader."); var availableAssets = assetLoader.GetAssets(fontFamilyKey.Source, fontFamilyKey.BaseUri); @@ -50,7 +51,8 @@ namespace Avalonia.Media.Fonts /// private static IEnumerable GetFontAssetsByExpression(FontFamilyKey fontFamilyKey) { - var assetLoader = AvaloniaLocator.Current.GetService(); + var assetLoader = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IAssetLoader."); var fileName = GetFileName(fontFamilyKey, out var fileExtension, out var location); @@ -87,7 +89,7 @@ namespace Avalonia.Media.Fonts { fileExtension = "." + fontFamilyKey.Source.AbsolutePath.Split('.').LastOrDefault(); - var fileName = fontFamilyKey.Source.LocalPath.Replace(fileExtension, string.Empty).Split('.').LastOrDefault(); + var fileName = fontFamilyKey.Source.LocalPath.Replace(fileExtension, string.Empty).Split('.').Last(); location = new Uri(fontFamilyKey.Source.AbsoluteUri.Replace("." + fileName + fileExtension, string.Empty), UriKind.RelativeOrAbsolute); diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs index ddffbe7500..499761d96c 100644 --- a/src/Avalonia.Visuals/Media/FormattedText.cs +++ b/src/Avalonia.Visuals/Media/FormattedText.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Avalonia.Platform; @@ -10,11 +11,11 @@ namespace Avalonia.Media { private readonly IPlatformRenderInterface _platform; private Size _constraint = Size.Infinity; - private IFormattedTextImpl _platformImpl; - private IReadOnlyList _spans; + private IFormattedTextImpl? _platformImpl; + private IReadOnlyList? _spans; private Typeface _typeface; private double _fontSize; - private string _text; + private string? _text; private TextAlignment _textAlignment; private TextWrapping _textWrapping; @@ -23,7 +24,8 @@ namespace Avalonia.Media /// public FormattedText() { - _platform = AvaloniaLocator.Current.GetService(); + _platform = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); } /// @@ -98,7 +100,7 @@ namespace Avalonia.Media /// Gets or sets a collection of spans that describe the formatting of subsections of the /// text. /// - public IReadOnlyList Spans + public IReadOnlyList? Spans { get => _spans; set => Set(ref _spans, value); @@ -107,7 +109,7 @@ namespace Avalonia.Media /// /// Gets or sets the text. /// - public string Text + public string? Text { get => _text; set => Set(ref _text, value); @@ -141,7 +143,7 @@ namespace Avalonia.Media if (_platformImpl == null) { _platformImpl = _platform.CreateFormattedText( - _text, + _text ?? string.Empty, _typeface, _fontSize, _textAlignment, diff --git a/src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs b/src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs index 92fdd651c3..fcb631d1eb 100644 --- a/src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs +++ b/src/Avalonia.Visuals/Media/FormattedTextStyleSpan.cs @@ -14,7 +14,7 @@ public FormattedTextStyleSpan( int startIndex, int length, - IBrush foregroundBrush = null) + IBrush? foregroundBrush = null) { StartIndex = startIndex; Length = length; @@ -34,6 +34,6 @@ /// /// Gets the span's foreground brush. /// - public IBrush ForegroundBrush { get; } + public IBrush? ForegroundBrush { get; } } } diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Visuals/Media/Geometry.cs index ccef6665a9..76c67a5cf4 100644 --- a/src/Avalonia.Visuals/Media/Geometry.cs +++ b/src/Avalonia.Visuals/Media/Geometry.cs @@ -11,11 +11,11 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty TransformProperty = - AvaloniaProperty.Register(nameof(Transform)); + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); private bool _isDirty = true; - private IGeometryImpl _platformImpl; + private IGeometryImpl? _platformImpl; static Geometry() { @@ -25,7 +25,7 @@ namespace Avalonia.Media /// /// Raised when the geometry changes. /// - public event EventHandler Changed; + public event EventHandler? Changed; /// /// Gets the geometry's bounding rectangle. @@ -35,7 +35,7 @@ namespace Avalonia.Media /// /// Gets the platform-specific implementation of the geometry. /// - public IGeometryImpl PlatformImpl + public IGeometryImpl? PlatformImpl { get { @@ -60,7 +60,7 @@ namespace Avalonia.Media /// /// Gets or sets a transform to apply to the geometry. /// - public Transform Transform + public Transform? Transform { get { return GetValue(TransformProperty); } set { SetValue(TransformProperty, value); } @@ -127,7 +127,7 @@ namespace Avalonia.Media /// Creates the platform implementation of the geometry, without the transform applied. /// /// - protected abstract IGeometryImpl CreateDefiningGeometry(); + protected abstract IGeometryImpl? CreateDefiningGeometry(); /// /// Invalidates the platform implementation of the geometry. @@ -141,8 +141,8 @@ namespace Avalonia.Media private void TransformChanged(AvaloniaPropertyChangedEventArgs e) { - var oldValue = (Transform)e.OldValue; - var newValue = (Transform)e.NewValue; + var oldValue = (Transform?)e.OldValue; + var newValue = (Transform?)e.NewValue; if (oldValue != null) { @@ -157,9 +157,9 @@ namespace Avalonia.Media TransformChanged(newValue, EventArgs.Empty); } - private void TransformChanged(object sender, EventArgs e) + private void TransformChanged(object? sender, EventArgs e) { - var transform = ((Transform)sender)?.Value; + var transform = ((Transform?)sender)?.Value; if (_platformImpl is ITransformedGeometryImpl t) { @@ -174,7 +174,7 @@ namespace Avalonia.Media } else if (_platformImpl != null && transform != null && transform != Matrix.Identity) { - _platformImpl = PlatformImpl.WithTransform(transform.Value); + _platformImpl = _platformImpl.WithTransform(transform.Value); } Changed?.Invoke(this, EventArgs.Empty); diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Visuals/Media/GlyphRun.cs index 234122f6f5..e15a19306a 100644 --- a/src/Avalonia.Visuals/Media/GlyphRun.cs +++ b/src/Avalonia.Visuals/Media/GlyphRun.cs @@ -14,7 +14,7 @@ namespace Avalonia.Media private static readonly IComparer s_ascendingComparer = Comparer.Default; private static readonly IComparer s_descendingComparer = new ReverseComparer(); - private IGlyphRunImpl _glyphRunImpl; + private IGlyphRunImpl? _glyphRunImpl; private GlyphTypeface _glyphTypeface; private double _fontRenderingEmSize; private int _biDiLevel; @@ -27,14 +27,6 @@ namespace Avalonia.Media private ReadOnlySlice _glyphClusters; private ReadOnlySlice _characters; - /// - /// Initializes a new instance of the class. - /// - public GlyphRun() - { - - } - /// /// Initializes a new instance of the class by specifying properties of the class. /// @@ -56,7 +48,7 @@ namespace Avalonia.Media ReadOnlySlice glyphClusters = default, int biDiLevel = 0) { - GlyphTypeface = glyphTypeface; + _glyphTypeface = glyphTypeface; FontRenderingEmSize = fontRenderingEmSize; @@ -74,13 +66,9 @@ namespace Avalonia.Media } /// - /// Gets or sets the for the . + /// Gets the for the . /// - public GlyphTypeface GlyphTypeface - { - get => _glyphTypeface; - set => Set(ref _glyphTypeface, value); - } + public GlyphTypeface GlyphTypeface => _glyphTypeface; /// /// Gets or sets the em size used for rendering the . @@ -199,7 +187,7 @@ namespace Avalonia.Media Initialize(); } - return _glyphRunImpl; + return _glyphRunImpl!; } } @@ -639,7 +627,8 @@ namespace Avalonia.Media throw new InvalidOperationException(); } - var platformRenderInterface = AvaloniaLocator.Current.GetService(); + var platformRenderInterface = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface"); _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this); } @@ -651,7 +640,7 @@ namespace Avalonia.Media private class ReverseComparer : IComparer { - public int Compare(T x, T y) + public int Compare(T? x, T? y) { return Comparer.Default.Compare(y, x); } diff --git a/src/Avalonia.Visuals/Media/GlyphTypeface.cs b/src/Avalonia.Visuals/Media/GlyphTypeface.cs index 2be505b9c0..67dfbb84b6 100644 --- a/src/Avalonia.Visuals/Media/GlyphTypeface.cs +++ b/src/Avalonia.Visuals/Media/GlyphTypeface.cs @@ -8,8 +8,8 @@ namespace Avalonia.Media public const int InvisibleGlyph = 3; public GlyphTypeface(Typeface typeface) - : this(FontManager.Current?.PlatformImpl.CreateGlyphTypeface(typeface)) - { + : this(FontManager.Current.PlatformImpl.CreateGlyphTypeface(typeface)) + { } public GlyphTypeface(IGlyphTypefaceImpl platformImpl) diff --git a/src/Avalonia.Visuals/Media/GradientBrush.cs b/src/Avalonia.Visuals/Media/GradientBrush.cs index 5f795d4644..c84413ecbb 100644 --- a/src/Avalonia.Visuals/Media/GradientBrush.cs +++ b/src/Avalonia.Visuals/Media/GradientBrush.cs @@ -26,7 +26,7 @@ namespace Avalonia.Media public static readonly StyledProperty GradientStopsProperty = AvaloniaProperty.Register(nameof(GradientStops)); - private IDisposable _gradientStopsSubscription; + private IDisposable? _gradientStopsSubscription; static GradientBrush() { @@ -64,13 +64,13 @@ namespace Avalonia.Media { if (e.Sender is GradientBrush brush) { - var oldValue = (GradientStops)e.OldValue; - var newValue = (GradientStops)e.NewValue; + var oldValue = (GradientStops?)e.OldValue; + var newValue = (GradientStops?)e.NewValue; if (oldValue != null) { oldValue.CollectionChanged -= brush.GradientStopsChanged; - brush._gradientStopsSubscription.Dispose(); + brush._gradientStopsSubscription?.Dispose(); } if (newValue != null) @@ -83,12 +83,12 @@ namespace Avalonia.Media } } - private void GradientStopsChanged(object sender, NotifyCollectionChangedEventArgs e) + private void GradientStopsChanged(object? sender, NotifyCollectionChangedEventArgs e) { RaiseInvalidated(EventArgs.Empty); } - private void GradientStopChanged(Tuple e) + private void GradientStopChanged(Tuple e) { RaiseInvalidated(EventArgs.Empty); } diff --git a/src/Avalonia.Visuals/Media/IPen.cs b/src/Avalonia.Visuals/Media/IPen.cs index 0cdac312cc..1cad9948b4 100644 --- a/src/Avalonia.Visuals/Media/IPen.cs +++ b/src/Avalonia.Visuals/Media/IPen.cs @@ -8,12 +8,12 @@ /// /// Gets the brush used to draw the stroke. /// - IBrush Brush { get; } + IBrush? Brush { get; } /// /// Gets the style of dashed lines drawn with a object. /// - IDashStyle DashStyle { get; } + IDashStyle? DashStyle { get; } /// /// Gets the type of shape to use on both ends of a line. diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs index 5a0c57b333..020940d9c6 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs @@ -21,8 +21,7 @@ namespace Avalonia.Media.Imaging /// An instance of the class. public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); - return new Bitmap(factory.LoadBitmapToWidth(stream, width, interpolationMode)); + return new Bitmap(GetFactory().LoadBitmapToWidth(stream, width, interpolationMode)); } /// @@ -35,8 +34,7 @@ namespace Avalonia.Media.Imaging /// An instance of the class. public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); - return new Bitmap(factory.LoadBitmapToHeight(stream, height, interpolationMode)); + return new Bitmap(GetFactory().LoadBitmapToHeight(stream, height, interpolationMode)); } /// @@ -47,8 +45,7 @@ namespace Avalonia.Media.Imaging /// An instance of the class. public Bitmap CreateScaledBitmap(PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); - return new Bitmap(factory.ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode)); + return new Bitmap(GetFactory().ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode)); } /// @@ -57,8 +54,7 @@ namespace Avalonia.Media.Imaging /// The filename of the bitmap. public Bitmap(string fileName) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); - PlatformImpl = RefCountable.Create(factory.LoadBitmap(fileName)); + PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(fileName)); } /// @@ -67,8 +63,7 @@ namespace Avalonia.Media.Imaging /// The stream to read the bitmap from. public Bitmap(Stream stream) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); - PlatformImpl = RefCountable.Create(factory.LoadBitmap(stream)); + PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(stream)); } /// @@ -106,9 +101,8 @@ namespace Avalonia.Media.Imaging [Obsolete("Use overload taking an AlphaFormat.")] public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) { - var ri = AvaloniaLocator.Current.GetService(); - - PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService() + var ri = GetFactory(); + PlatformImpl = RefCountable.Create(ri .LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride)); } @@ -123,8 +117,7 @@ namespace Avalonia.Media.Imaging /// The number of bytes per row. public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { - PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService() - .LoadBitmap(format, alphaFormat, data, size, dpi, stride)); + PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(format, alphaFormat, data, size, dpi, stride)); } /// @@ -173,5 +166,11 @@ namespace Avalonia.Media.Imaging destRect, bitmapInterpolationMode); } + + private static IPlatformRenderInterface GetFactory() + { + return AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + } } } diff --git a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs index 70823e114c..be1f6db561 100644 --- a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs @@ -11,8 +11,8 @@ namespace Avalonia.Media.Imaging /// /// Defines the property. /// - public static readonly StyledProperty SourceProperty = - AvaloniaProperty.Register(nameof(Source)); + public static readonly StyledProperty SourceProperty = + AvaloniaProperty.Register(nameof(Source)); /// /// Defines the property. @@ -20,7 +20,7 @@ namespace Avalonia.Media.Imaging public static readonly StyledProperty SourceRectProperty = AvaloniaProperty.Register(nameof(SourceRect)); - public event EventHandler Invalidated; + public event EventHandler? Invalidated; static CroppedBitmap() { @@ -31,7 +31,7 @@ namespace Avalonia.Media.Imaging /// /// Gets or sets the source for the bitmap. /// - public IImage Source + public IImage? Source { get => GetValue(SourceProperty); set => SetValue(SourceProperty, value); @@ -77,19 +77,19 @@ namespace Avalonia.Media.Imaging public Size Size { get { - if (Source == null) + if (Source is not IBitmap bmp) return Size.Empty; if (SourceRect.IsEmpty) return Source.Size; - return SourceRect.Size.ToSizeWithDpi((Source as IBitmap).Dpi); + return SourceRect.Size.ToSizeWithDpi(bmp.Dpi); } } public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode) { - if (Source == null) + if (Source is not IBitmap bmp) return; - var topLeft = SourceRect.TopLeft.ToPointWithDpi((Source as IBitmap).Dpi); + var topLeft = SourceRect.TopLeft.ToPointWithDpi(bmp.Dpi); Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode); } } diff --git a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs index 928b188f8c..fe8890b3ba 100644 --- a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs @@ -54,11 +54,12 @@ namespace Avalonia.Media.Imaging /// The platform-specific implementation. private static IRenderTargetBitmapImpl CreateImpl(PixelSize size, Vector dpi) { - IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService(); + IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); return factory.CreateRenderTargetBitmap(size, dpi); } /// - public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer vbr) => PlatformImpl.Item.CreateDrawingContext(vbr); + public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? vbr) => PlatformImpl.Item.CreateDrawingContext(vbr); } } diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs index b0e1a954e6..4dfb22440d 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs @@ -46,7 +46,7 @@ namespace Avalonia.Media.Imaging public static WriteableBitmap Decode(Stream stream) { - var ri = AvaloniaLocator.Current.GetService(); + var ri = GetFactory(); return new WriteableBitmap(ri.LoadWriteableBitmap(stream)); } @@ -61,7 +61,7 @@ namespace Avalonia.Media.Imaging /// An instance of the class. public new static WriteableBitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { - var ri = AvaloniaLocator.Current.GetService(); + var ri = GetFactory(); return new WriteableBitmap(ri.LoadWriteableBitmapToWidth(stream, width, interpolationMode)); } @@ -76,19 +76,25 @@ namespace Avalonia.Media.Imaging /// An instance of the class. public new static WriteableBitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality) { - var ri = AvaloniaLocator.Current.GetService(); + var ri = GetFactory(); return new WriteableBitmap(ri.LoadWriteableBitmapToHeight(stream, height, interpolationMode)); } private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat) { - var ri = AvaloniaLocator.Current.GetService(); + var ri = GetFactory(); PixelFormat finalFormat = format ?? ri.DefaultPixelFormat; AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat; return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat); } + + private static IPlatformRenderInterface GetFactory() + { + return AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); + } } } diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs index 2dd188e0a9..82485c13b0 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs @@ -30,10 +30,10 @@ namespace Avalonia.Media.Immutable public double Offset { get; } /// - public override bool Equals(object obj) => Equals(obj as IDashStyle); + public override bool Equals(object? obj) => Equals(obj as IDashStyle); /// - public bool Equals(IDashStyle other) + public bool Equals(IDashStyle? other) { if (ReferenceEquals(this, other)) { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs index 3256f4b11a..8a53eaf7b8 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs @@ -21,7 +21,7 @@ namespace Avalonia.Media.Immutable public ImmutablePen( uint color, double thickness = 1.0, - ImmutableDashStyle dashStyle = null, + ImmutableDashStyle? dashStyle = null, PenLineCap lineCap = PenLineCap.Flat, PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) : this(new ImmutableSolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit) @@ -38,9 +38,9 @@ namespace Avalonia.Media.Immutable /// The line join. /// The miter limit. public ImmutablePen( - IBrush brush, + IBrush? brush, double thickness = 1.0, - ImmutableDashStyle dashStyle = null, + ImmutableDashStyle? dashStyle = null, PenLineCap lineCap = PenLineCap.Flat, PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) @@ -58,7 +58,7 @@ namespace Avalonia.Media.Immutable /// /// Gets the brush used to draw the stroke. /// - public IBrush Brush { get; } + public IBrush? Brush { get; } /// /// Gets the stroke thickness. @@ -68,7 +68,7 @@ namespace Avalonia.Media.Immutable /// /// Specifies the style of dashed lines drawn with a object. /// - public IDashStyle DashStyle { get; } + public IDashStyle? DashStyle { get; } /// /// Specifies the type of graphic shape to use on both ends of a line. @@ -87,10 +87,10 @@ namespace Avalonia.Media.Immutable public double MiterLimit { get; } /// - public override bool Equals(object obj) => Equals(obj as IPen); + public override bool Equals(object? obj) => Equals(obj as IPen); /// - public bool Equals(IPen other) + public bool Equals(IPen? other) { if (ReferenceEquals(this, other)) { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs index 8e93ac580e..46e1df4054 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs @@ -46,14 +46,14 @@ namespace Avalonia.Media.Immutable /// public double Opacity { get; } - public bool Equals(ImmutableSolidColorBrush other) + public bool Equals(ImmutableSolidColorBrush? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Color.Equals(other.Color) && Opacity.Equals(other.Opacity); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ImmutableSolidColorBrush other && Equals(other); } diff --git a/src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs index f46d76cf3f..f8487243de 100644 --- a/src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs @@ -34,7 +34,7 @@ namespace Avalonia.Media } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is ImmutableExperimentalAcrylicMaterial other && Equals(other); } diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Visuals/Media/KnownColors.cs index fe09f5f538..8f07b5f8ac 100644 --- a/src/Avalonia.Visuals/Media/KnownColors.cs +++ b/src/Avalonia.Visuals/Media/KnownColors.cs @@ -20,7 +20,7 @@ namespace Avalonia.Media foreach (var field in typeof(KnownColor).GetRuntimeFields()) { if (field.FieldType != typeof(KnownColor)) continue; - var knownColor = (KnownColor)field.GetValue(null); + var knownColor = (KnownColor)field.GetValue(null)!; if (knownColor == KnownColor.None) continue; knownColorNames.Add(field.Name, knownColor); @@ -41,7 +41,7 @@ namespace Avalonia.Media } #if !BUILDTASK - public static ISolidColorBrush GetKnownBrush(string s) + public static ISolidColorBrush? GetKnownBrush(string s) { var color = GetKnownColor(s); return color != KnownColor.None ? color.ToBrush() : null; @@ -58,7 +58,7 @@ namespace Avalonia.Media return KnownColor.None; } - public static string GetKnownColorName(uint rgb) + public static string? GetKnownColorName(uint rgb) { return _knownColors.TryGetValue(rgb, out var name) ? name : null; } @@ -230,4 +230,4 @@ namespace Avalonia.Media Yellow = 0xffffff00, YellowGreen = 0xff9acd32 } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Visuals/Media/LineGeometry.cs index 94b638114b..b01a223aca 100644 --- a/src/Avalonia.Visuals/Media/LineGeometry.cs +++ b/src/Avalonia.Visuals/Media/LineGeometry.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Platform; namespace Avalonia.Media @@ -67,9 +68,10 @@ namespace Avalonia.Media } /// - protected override IGeometryImpl CreateDefiningGeometry() + protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); return factory.CreateLineGeometry(StartPoint, EndPoint); } diff --git a/src/Avalonia.Visuals/Media/MaterialExtensions.cs b/src/Avalonia.Visuals/Media/MaterialExtensions.cs index c0b445c357..0b3d86d0a4 100644 --- a/src/Avalonia.Visuals/Media/MaterialExtensions.cs +++ b/src/Avalonia.Visuals/Media/MaterialExtensions.cs @@ -14,7 +14,7 @@ namespace Avalonia.Media /// public static IExperimentalAcrylicMaterial ToImmutable(this IExperimentalAcrylicMaterial material) { - Contract.Requires(material != null); + _ = material ?? throw new ArgumentNullException(nameof(material)); return (material as IMutableExperimentalAcrylicMaterial)?.ToImmutable() ?? material; } diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Visuals/Media/PathGeometry.cs index 3d11c19b7d..0b6a157280 100644 --- a/src/Avalonia.Visuals/Media/PathGeometry.cs +++ b/src/Avalonia.Visuals/Media/PathGeometry.cs @@ -11,8 +11,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly DirectProperty FiguresProperty = - AvaloniaProperty.RegisterDirect(nameof(Figures), g => g.Figures, (g, f) => g.Figures = f); + public static readonly DirectProperty FiguresProperty = + AvaloniaProperty.RegisterDirect(nameof(Figures), g => g.Figures, (g, f) => g.Figures = f); /// /// Defines the property. @@ -20,9 +20,9 @@ namespace Avalonia.Media public static readonly StyledProperty FillRuleProperty = AvaloniaProperty.Register(nameof(FillRule)); - private PathFigures _figures; - private IDisposable _figuresObserver; - private IDisposable _figuresPropertiesObserver; + private PathFigures? _figures; + private IDisposable? _figuresObserver; + private IDisposable? _figuresPropertiesObserver; static PathGeometry() { @@ -35,7 +35,7 @@ namespace Avalonia.Media /// public PathGeometry() { - Figures = new PathFigures(); + _figures = new PathFigures(); } /// @@ -63,7 +63,7 @@ namespace Avalonia.Media /// The figures. /// [Content] - public PathFigures Figures + public PathFigures? Figures { get { return _figures; } set { SetAndRaise(FiguresProperty, ref _figures, value); } @@ -81,15 +81,21 @@ namespace Avalonia.Media set { SetValue(FillRuleProperty, value); } } - protected override IGeometryImpl CreateDefiningGeometry() + protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService(); + var figures = Figures; + + if (figures is null) + return null; + + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); var geometry = factory.CreateStreamGeometry(); using (var ctx = new StreamGeometryContext(geometry.Open())) { ctx.SetFillRule(FillRule); - foreach (var f in Figures) + foreach (var f in figures) { f.ApplyTo(ctx); } @@ -98,7 +104,7 @@ namespace Avalonia.Media return geometry; } - private void OnFiguresChanged(PathFigures figures) + private void OnFiguresChanged(PathFigures? figures) { _figuresObserver?.Dispose(); _figuresPropertiesObserver?.Dispose(); @@ -120,12 +126,15 @@ namespace Avalonia.Media } - private void InvalidateGeometryFromSegments(object _, EventArgs __) + private void InvalidateGeometryFromSegments(object? _, EventArgs __) { InvalidateGeometry(); } public override string ToString() - => $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}"; + { + var figuresString = _figures is not null ? string.Join(" ", _figures) : string.Empty; + return $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{figuresString}"; + } } } diff --git a/src/Avalonia.Visuals/Media/PathGeometryCollections.cs b/src/Avalonia.Visuals/Media/PathGeometryCollections.cs index 1589d2f5be..1165b192a7 100644 --- a/src/Avalonia.Visuals/Media/PathGeometryCollections.cs +++ b/src/Avalonia.Visuals/Media/PathGeometryCollections.cs @@ -20,11 +20,11 @@ namespace Avalonia.Media parser.Parse(pathData); } - return pathGeometry.Figures; + return pathGeometry.Figures!; } } public sealed class PathSegments : AvaloniaList { } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index 8b9d0833db..30c5206125 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using Avalonia.Platform; @@ -27,7 +28,7 @@ namespace Avalonia.Media { 'Z', Command.Close }, }; - private IGeometryContext _geometryContext; + private IGeometryContext? _geometryContext; private Point _currentPoint; private Point? _beginFigurePoint; private Point? _previousControlPoint; @@ -98,6 +99,8 @@ namespace Avalonia.Media /// The path data. public void Parse(string pathData) { + ThrowIfDisposed(); + var span = pathData.AsSpan(); _currentPoint = new Point(); @@ -171,6 +174,8 @@ namespace Avalonia.Media private void CreateFigure() { + ThrowIfDisposed(); + if (_isOpen) { _geometryContext.EndFigure(false); @@ -185,6 +190,8 @@ namespace Avalonia.Media private void SetFillRule(ref ReadOnlySpan span) { + ThrowIfDisposed(); + if (!ReadArgument(ref span, out var fillRule) || fillRule.Length != 1) { throw new InvalidDataException("Invalid fill rule."); @@ -209,6 +216,8 @@ namespace Avalonia.Media private void CloseFigure() { + ThrowIfDisposed(); + if (_isOpen) { _geometryContext.EndFigure(true); @@ -244,6 +253,8 @@ namespace Avalonia.Media private void AddLine(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + _currentPoint = relative ? ReadRelativePoint(ref span, _currentPoint) : ReadPoint(ref span); @@ -258,6 +269,8 @@ namespace Avalonia.Media private void AddHorizontalLine(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + _currentPoint = relative ? new Point(_currentPoint.X + ReadDouble(ref span), _currentPoint.Y) : _currentPoint.WithX(ReadDouble(ref span)); @@ -272,6 +285,8 @@ namespace Avalonia.Media private void AddVerticalLine(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + _currentPoint = relative ? new Point(_currentPoint.X, _currentPoint.Y + ReadDouble(ref span)) : _currentPoint.WithY(ReadDouble(ref span)); @@ -286,6 +301,8 @@ namespace Avalonia.Media private void AddCubicBezierCurve(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + var point1 = relative ? ReadRelativePoint(ref span, _currentPoint) : ReadPoint(ref span); @@ -316,6 +333,8 @@ namespace Avalonia.Media private void AddQuadraticBezierCurve(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + var start = relative ? ReadRelativePoint(ref span, _currentPoint) : ReadPoint(ref span); @@ -340,6 +359,8 @@ namespace Avalonia.Media private void AddSmoothCubicBezierCurve(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + var point2 = relative ? ReadRelativePoint(ref span, _currentPoint) : ReadPoint(ref span); @@ -369,6 +390,8 @@ namespace Avalonia.Media private void AddSmoothQuadraticBezierCurve(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + var end = relative ? ReadRelativePoint(ref span, _currentPoint) : ReadPoint(ref span); @@ -390,6 +413,8 @@ namespace Avalonia.Media private void AddArc(ref ReadOnlySpan span, bool relative) { + ThrowIfDisposed(); + var size = ReadSize(ref span); span = ReadSeparator(span); @@ -570,5 +595,12 @@ namespace Avalonia.Media span = span.Slice(1); return true; } + + [MemberNotNull(nameof(_geometryContext))] + private void ThrowIfDisposed() + { + if (_isDisposed || _geometryContext is null) + throw new ObjectDisposedException(nameof(PathMarkupParser)); + } } } diff --git a/src/Avalonia.Visuals/Media/Pen.cs b/src/Avalonia.Visuals/Media/Pen.cs index eb44b3f21e..7c966a35cf 100644 --- a/src/Avalonia.Visuals/Media/Pen.cs +++ b/src/Avalonia.Visuals/Media/Pen.cs @@ -12,8 +12,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty BrushProperty = - AvaloniaProperty.Register(nameof(Brush)); + public static readonly StyledProperty BrushProperty = + AvaloniaProperty.Register(nameof(Brush)); /// /// Defines the property. @@ -24,8 +24,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty DashStyleProperty = - AvaloniaProperty.Register(nameof(DashStyle)); + public static readonly StyledProperty DashStyleProperty = + AvaloniaProperty.Register(nameof(DashStyle)); /// /// Defines the property. @@ -64,7 +64,7 @@ namespace Avalonia.Media public Pen( uint color, double thickness = 1.0, - IDashStyle dashStyle = null, + IDashStyle? dashStyle = null, PenLineCap lineCap = PenLineCap.Flat, PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) : this(new SolidColorBrush(color), thickness, dashStyle, lineCap, lineJoin, miterLimit) @@ -81,9 +81,9 @@ namespace Avalonia.Media /// The line join. /// The miter limit. public Pen( - IBrush brush, + IBrush? brush, double thickness = 1.0, - IDashStyle dashStyle = null, + IDashStyle? dashStyle = null, PenLineCap lineCap = PenLineCap.Flat, PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) @@ -110,7 +110,7 @@ namespace Avalonia.Media /// /// Gets or sets the brush used to draw the stroke. /// - public IBrush Brush + public IBrush? Brush { get => GetValue(BrushProperty); set => SetValue(BrushProperty, value); @@ -128,7 +128,7 @@ namespace Avalonia.Media /// /// Gets or sets the style of dashed lines drawn with a object. /// - public IDashStyle DashStyle + public IDashStyle? DashStyle { get => GetValue(DashStyleProperty); set => SetValue(DashStyleProperty, value); @@ -165,7 +165,7 @@ namespace Avalonia.Media /// /// Raised when the pen changes. /// - public event EventHandler Invalidated; + public event EventHandler? Invalidated; /// /// Creates an immutable clone of the brush. @@ -244,6 +244,6 @@ namespace Avalonia.Media /// The event args. protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e); - private void AffectsRenderInvalidated(object sender, EventArgs e) => RaiseInvalidated(EventArgs.Empty); + private void AffectsRenderInvalidated(object? sender, EventArgs e) => RaiseInvalidated(EventArgs.Empty); } } diff --git a/src/Avalonia.Visuals/Media/PixelPoint.cs b/src/Avalonia.Visuals/Media/PixelPoint.cs index 7d62d7bc23..502eb205a6 100644 --- a/src/Avalonia.Visuals/Media/PixelPoint.cs +++ b/src/Avalonia.Visuals/Media/PixelPoint.cs @@ -144,7 +144,7 @@ namespace Avalonia /// /// True if is a point that equals the current point. /// - public override bool Equals(object obj) => obj is PixelPoint other && Equals(other); + public override bool Equals(object? obj) => obj is PixelPoint other && Equals(other); /// /// Returns a hash code for a . diff --git a/src/Avalonia.Visuals/Media/PixelRect.cs b/src/Avalonia.Visuals/Media/PixelRect.cs index 32a728475f..0059a4b483 100644 --- a/src/Avalonia.Visuals/Media/PixelRect.cs +++ b/src/Avalonia.Visuals/Media/PixelRect.cs @@ -208,7 +208,7 @@ namespace Avalonia /// /// The object to compare against. /// True if the object is equal to this rectangle; false otherwise. - public override bool Equals(object obj) => obj is PixelRect other && Equals(other); + public override bool Equals(object? obj) => obj is PixelRect other && Equals(other); /// /// Returns the hash code for this instance. diff --git a/src/Avalonia.Visuals/Media/PixelSize.cs b/src/Avalonia.Visuals/Media/PixelSize.cs index 530b01ed90..5a34c6f6b5 100644 --- a/src/Avalonia.Visuals/Media/PixelSize.cs +++ b/src/Avalonia.Visuals/Media/PixelSize.cs @@ -94,7 +94,7 @@ namespace Avalonia /// /// True if is a size that equals the current size. /// - public override bool Equals(object obj) => obj is PixelSize other && Equals(other); + public override bool Equals(object? obj) => obj is PixelSize other && Equals(other); /// /// Returns a hash code for a . diff --git a/src/Avalonia.Visuals/Media/PixelVector.cs b/src/Avalonia.Visuals/Media/PixelVector.cs index f32e9c0178..79e7b94c21 100644 --- a/src/Avalonia.Visuals/Media/PixelVector.cs +++ b/src/Avalonia.Visuals/Media/PixelVector.cs @@ -143,7 +143,7 @@ namespace Avalonia return Math.Abs(_x - other._x) < tolerance && Math.Abs(_y - other._y) < tolerance; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; diff --git a/src/Avalonia.Visuals/Media/PolylineGeometry.cs b/src/Avalonia.Visuals/Media/PolylineGeometry.cs index 94c809d432..8e040a6043 100644 --- a/src/Avalonia.Visuals/Media/PolylineGeometry.cs +++ b/src/Avalonia.Visuals/Media/PolylineGeometry.cs @@ -24,7 +24,7 @@ namespace Avalonia.Media AvaloniaProperty.Register(nameof(IsFilled)); private Points _points; - private IDisposable _pointsObserver; + private IDisposable? _pointsObserver; static PolylineGeometry() { @@ -37,7 +37,7 @@ namespace Avalonia.Media /// public PolylineGeometry() { - Points = new Points(); + _points = new Points(); } /// @@ -74,9 +74,10 @@ namespace Avalonia.Media return new PolylineGeometry(Points, IsFilled); } - protected override IGeometryImpl CreateDefiningGeometry() + protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); var geometry = factory.CreateStreamGeometry(); using (var context = geometry.Open()) @@ -97,7 +98,7 @@ namespace Avalonia.Media return geometry; } - private void OnPointsChanged(Points newValue) + private void OnPointsChanged(Points? newValue) { _pointsObserver?.Dispose(); _pointsObserver = newValue?.ForEachItem( diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Visuals/Media/RectangleGeometry.cs index 743e103a88..97b4869d71 100644 --- a/src/Avalonia.Visuals/Media/RectangleGeometry.cs +++ b/src/Avalonia.Visuals/Media/RectangleGeometry.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Platform; namespace Avalonia.Media @@ -46,9 +47,10 @@ namespace Avalonia.Media /// public override Geometry Clone() => new RectangleGeometry(Rect); - protected override IGeometryImpl CreateDefiningGeometry() + protected override IGeometryImpl? CreateDefiningGeometry() { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); return factory.CreateRectangleGeometry(Rect); } diff --git a/src/Avalonia.Visuals/Media/StreamGeometry.cs b/src/Avalonia.Visuals/Media/StreamGeometry.cs index be914cc7b2..b034ff7823 100644 --- a/src/Avalonia.Visuals/Media/StreamGeometry.cs +++ b/src/Avalonia.Visuals/Media/StreamGeometry.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Platform; namespace Avalonia.Media @@ -7,7 +8,7 @@ namespace Avalonia.Media /// public class StreamGeometry : Geometry { - IStreamGeometryImpl _impl; + IStreamGeometryImpl? _impl; /// /// Initializes a new instance of the class. @@ -46,7 +47,7 @@ namespace Avalonia.Media /// public override Geometry Clone() { - return new StreamGeometry(((IStreamGeometryImpl)PlatformImpl).Clone()); + return new StreamGeometry(((IStreamGeometryImpl)PlatformImpl!).Clone()); } /// @@ -57,15 +58,16 @@ namespace Avalonia.Media /// public StreamGeometryContext Open() { - return new StreamGeometryContext(((IStreamGeometryImpl)PlatformImpl).Open()); + return new StreamGeometryContext(((IStreamGeometryImpl)PlatformImpl!).Open()); } /// - protected override IGeometryImpl CreateDefiningGeometry() + protected override IGeometryImpl? CreateDefiningGeometry() { if (_impl == null) { - var factory = AvaloniaLocator.Current.GetService(); + var factory = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IPlatformRenderInterface."); _impl = factory.CreateStreamGeometry(); } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs index 7756ca5c8f..13d7b11645 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs @@ -10,9 +10,9 @@ namespace Avalonia.Media.TextFormatting private const double DefaultFontRenderingEmSize = 12; public GenericTextRunProperties(Typeface typeface, double fontRenderingEmSize = DefaultFontRenderingEmSize, - TextDecorationCollection textDecorations = null, IBrush foregroundBrush = null, - IBrush backgroundBrush = null, BaselineAlignment baselineAlignment = BaselineAlignment.Baseline, - CultureInfo cultureInfo = null) + TextDecorationCollection? textDecorations = null, IBrush? foregroundBrush = null, + IBrush? backgroundBrush = null, BaselineAlignment baselineAlignment = BaselineAlignment.Baseline, + CultureInfo? cultureInfo = null) { Typeface = typeface; FontRenderingEmSize = fontRenderingEmSize; @@ -30,18 +30,18 @@ namespace Avalonia.Media.TextFormatting public override double FontRenderingEmSize { get; } /// - public override TextDecorationCollection TextDecorations { get; } + public override TextDecorationCollection? TextDecorations { get; } /// - public override IBrush ForegroundBrush { get; } + public override IBrush? ForegroundBrush { get; } /// - public override IBrush BackgroundBrush { get; } + public override IBrush? BackgroundBrush { get; } /// public override BaselineAlignment BaselineAlignment { get; } /// - public override CultureInfo CultureInfo { get; } + public override CultureInfo? CultureInfo { get; } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs index 0f9994bc65..32012ab8e9 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs @@ -10,6 +10,6 @@ /// /// The text source index. /// The text run. - TextRun GetTextRun(int textSourceIndex); + TextRun? GetTextRun(int textSourceIndex); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs index 33d1b30e3c..72bb7431d7 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs @@ -166,7 +166,7 @@ namespace Avalonia.Media.TextFormatting public readonly struct SplitTextCharactersResult { - public SplitTextCharactersResult(ShapedTextCharacters first, ShapedTextCharacters second) + public SplitTextCharactersResult(ShapedTextCharacters first, ShapedTextCharacters? second) { First = first; @@ -187,7 +187,7 @@ namespace Avalonia.Media.TextFormatting /// /// The second text run. /// - public ShapedTextCharacters Second { get; } + public ShapedTextCharacters? Second { get; } } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs index e4c898e2b8..d521077a43 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs @@ -41,6 +41,6 @@ namespace Avalonia.Media.TextFormatting /// in terms of where the previous line in the paragraph was broken by the text formatting process. /// The formatted line. public abstract TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, - TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null); + TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs index df63b00c25..7c6af4eaa7 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs @@ -8,7 +8,7 @@ namespace Avalonia.Media.TextFormatting { /// public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, - TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak = null) + TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null) { var textWrapping = paragraphProperties.TextWrapping; @@ -241,7 +241,7 @@ namespace Avalonia.Media.TextFormatting first.Add(split.First); - second.Add(split.Second); + second.Add(split.Second!); if (secondCount > 0) { @@ -269,7 +269,7 @@ namespace Avalonia.Media.TextFormatting /// The formatted text runs. /// private static List FetchTextRuns(ITextSource textSource, - int firstTextSourceIndex, TextLineBreak previousLineBreak, out TextLineBreak nextLineBreak) + int firstTextSourceIndex, TextLineBreak? previousLineBreak, out TextLineBreak? nextLineBreak) { nextLineBreak = default; @@ -277,7 +277,7 @@ namespace Avalonia.Media.TextFormatting var textRuns = new List(); - if (previousLineBreak != null) + if (previousLineBreak?.RemainingCharacters != null) { for (var index = 0; index < previousLineBreak.RemainingCharacters.Count; index++) { @@ -298,11 +298,11 @@ namespace Avalonia.Media.TextFormatting { for (; index < previousLineBreak.RemainingCharacters.Count; index++) { - splitResult.Second.Add(previousLineBreak.RemainingCharacters[index]); + splitResult.Second!.Add(previousLineBreak.RemainingCharacters[index]); } } - nextLineBreak = new TextLineBreak(splitResult.Second); + nextLineBreak = new TextLineBreak(splitResult.Second!); return splitResult.First; } @@ -317,7 +317,7 @@ namespace Avalonia.Media.TextFormatting while (textRunEnumerator.MoveNext()) { - var textRun = textRunEnumerator.Current; + var textRun = textRunEnumerator.Current!; switch (textRun) { @@ -346,7 +346,7 @@ namespace Avalonia.Media.TextFormatting { var splitResult = SplitTextRuns(textRuns, currentLength + runLineBreak.PositionWrap); - nextLineBreak = new TextLineBreak(splitResult.Second); + nextLineBreak = new TextLineBreak(splitResult.Second!); return splitResult.First; } @@ -398,7 +398,7 @@ namespace Avalonia.Media.TextFormatting /// The current line break if the line was explicitly broken. /// The wrapped text line. private static TextLine PerformTextWrapping(List textRuns, TextRange textRange, - double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak currentLineBreak) + double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak? currentLineBreak) { var availableWidth = paragraphWidth; var currentWidth = 0.0; @@ -517,7 +517,7 @@ namespace Avalonia.Media.TextFormatting var lineBreak = remainingCharacters?.Count > 0 ? new TextLineBreak(remainingCharacters) : null; - if (lineBreak is null && currentLineBreak.TextEndOfLine != null) + if (lineBreak is null && currentLineBreak?.TextEndOfLine != null) { lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine); } @@ -553,7 +553,7 @@ namespace Avalonia.Media.TextFormatting internal readonly struct SplitTextRunsResult { - public SplitTextRunsResult(List first, List second) + public SplitTextRunsResult(List first, List? second) { First = first; @@ -574,7 +574,7 @@ namespace Avalonia.Media.TextFormatting /// /// The second text runs. /// - public List Second { get; } + public List? Second { get; } } private struct TextRunEnumerator @@ -590,7 +590,7 @@ namespace Avalonia.Media.TextFormatting } // ReSharper disable once MemberHidesStaticFromOuterClass - public TextRun Current { get; private set; } + public TextRun? Current { get; private set; } public bool MoveNext() { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index a4c9392fa0..40e12c8e99 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Avalonia.Utilities; @@ -14,7 +15,7 @@ namespace Avalonia.Media.TextFormatting private readonly ReadOnlySlice _text; private readonly TextParagraphProperties _paragraphProperties; - private readonly IReadOnlyList> _textStyleOverrides; + private readonly IReadOnlyList>? _textStyleOverrides; private readonly TextTrimming _textTrimming; /// @@ -41,12 +42,12 @@ namespace Avalonia.Media.TextFormatting TextAlignment textAlignment = TextAlignment.Left, TextWrapping textWrapping = TextWrapping.NoWrap, TextTrimming textTrimming = TextTrimming.None, - TextDecorationCollection textDecorations = null, + TextDecorationCollection? textDecorations = null, double maxWidth = double.PositiveInfinity, double maxHeight = double.PositiveInfinity, double lineHeight = double.NaN, int maxLines = 0, - IReadOnlyList> textStyleOverrides = null) + IReadOnlyList>? textStyleOverrides = null) { _text = string.IsNullOrEmpty(text) ? new ReadOnlySlice() : @@ -228,7 +229,7 @@ namespace Avalonia.Media.TextFormatting var currentY = 0d; var lineIndex = 0; - TextLine currentLine = null; + TextLine? currentLine = null; CharacterHit characterHit; for (; lineIndex < TextLines.Count; lineIndex++) @@ -289,7 +290,7 @@ namespace Avalonia.Media.TextFormatting /// private static TextParagraphProperties CreateTextParagraphProperties(Typeface typeface, double fontSize, IBrush foreground, TextAlignment textAlignment, TextWrapping textWrapping, - TextDecorationCollection textDecorations, double lineHeight) + TextDecorationCollection? textDecorations, double lineHeight) { var textRunStyle = new GenericTextRunProperties(typeface, fontSize, textDecorations, foreground); @@ -339,6 +340,7 @@ namespace Avalonia.Media.TextFormatting /// /// Updates the layout and applies specified text style overrides. /// + [MemberNotNull(nameof(TextLines))] private void UpdateLayout() { if (_text.IsEmpty || MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight)) @@ -360,7 +362,7 @@ namespace Avalonia.Media.TextFormatting var textSource = new FormattedTextSource(_text, _paragraphProperties.DefaultTextRunProperties, _textStyleOverrides); - TextLine previousLine = null; + TextLine? previousLine = null; while (currentPosition < _text.Length) { @@ -558,17 +560,17 @@ namespace Avalonia.Media.TextFormatting { private readonly ReadOnlySlice _text; private readonly TextRunProperties _defaultProperties; - private readonly IReadOnlyList> _textModifier; + private readonly IReadOnlyList>? _textModifier; public FormattedTextSource(ReadOnlySlice text, TextRunProperties defaultProperties, - IReadOnlyList> textModifier) + IReadOnlyList>? textModifier) { _text = text; _defaultProperties = defaultProperties; _textModifier = textModifier; } - public TextRun GetTextRun(int textSourceIndex) + public TextRun? GetTextRun(int textSourceIndex) { if (textSourceIndex > _text.Length) { @@ -597,7 +599,7 @@ namespace Avalonia.Media.TextFormatting /// The created text style run. /// private static ValueSpan CreateTextStyleRun(ReadOnlySlice text, - TextRunProperties defaultProperties, IReadOnlyList> textModifier) + TextRunProperties defaultProperties, IReadOnlyList>? textModifier) { if (textModifier == null || textModifier.Count == 0) { diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs index e343279a43..aea4227002 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs @@ -29,7 +29,7 @@ namespace Avalonia.Media.TextFormatting /// /// A value that represents the line break. /// - public abstract TextLineBreak TextLineBreak { get; } + public abstract TextLineBreak? TextLineBreak { get; } /// /// Gets the distance from the top to the baseline of the current TextLine object. diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs index 4ee0a9a28f..d2bd58682a 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs @@ -17,11 +17,11 @@ namespace Avalonia.Media.TextFormatting /// /// Get the /// - public TextEndOfLine TextEndOfLine { get; } + public TextEndOfLine? TextEndOfLine { get; } /// /// Get the remaining shaped characters that were split up by the during the formatting process. /// - public IReadOnlyList RemainingCharacters { get; } + public IReadOnlyList? RemainingCharacters { get; } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs index b68e50c54e..b1397518e4 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; using Avalonia.Utilities; @@ -13,7 +14,7 @@ namespace Avalonia.Media.TextFormatting private readonly TextLineMetrics _textLineMetrics; public TextLineImpl(List textRuns, TextRange textRange, double paragraphWidth, - TextParagraphProperties paragraphProperties, TextLineBreak lineBreak = null, bool hasCollapsed = false) + TextParagraphProperties paragraphProperties, TextLineBreak? lineBreak = null, bool hasCollapsed = false) { TextRange = textRange; TextLineBreak = lineBreak; @@ -33,7 +34,7 @@ namespace Avalonia.Media.TextFormatting public override TextRange TextRange { get; } /// - public override TextLineBreak TextLineBreak { get; } + public override TextLineBreak? TextLineBreak { get; } /// public override bool HasCollapsed { get; } @@ -479,12 +480,13 @@ namespace Avalonia.Media.TextFormatting /// internal static ShapedTextCharacters CreateShapedSymbol(TextRun textRun) { - var formatterImpl = AvaloniaLocator.Current.GetService(); + var properties = textRun.Properties; - var glyphRun = formatterImpl.ShapeText(textRun.Text, textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize, - textRun.Properties.CultureInfo); + _ = properties ?? throw new InvalidOperationException($"{nameof(TextRun.Properties)} should not be null."); - return new ShapedTextCharacters(glyphRun, textRun.Properties); + var glyphRun = TextShaper.Current.ShapeText(textRun.Text, properties.Typeface, properties.FontRenderingEmSize, properties.CultureInfo); + + return new ShapedTextCharacters(glyphRun, properties); } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs index 4eff3fbd83..bbff09ad79 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs @@ -44,7 +44,7 @@ /// If not null, text decorations to apply to all runs in the line. This is in addition /// to any text decorations specified by the TextRunProperties for individual text runs. /// - public virtual TextDecorationCollection TextDecorations => null; + public virtual TextDecorationCollection? TextDecorations => null; /// /// Gets the text wrapping. diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs index 42cb5a7c46..4bfbb89006 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs @@ -24,7 +24,7 @@ namespace Avalonia.Media.TextFormatting /// /// A set of properties shared by every characters in the run /// - public virtual TextRunProperties Properties => null; + public virtual TextRunProperties? Properties => null; private class TextRunDebuggerProxy { @@ -49,7 +49,7 @@ namespace Avalonia.Media.TextFormatting } } - public TextRunProperties Properties => _textRun.Properties; + public TextRunProperties? Properties => _textRun.Properties; } } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs index 468df33ab1..513778b596 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs @@ -25,29 +25,29 @@ namespace Avalonia.Media.TextFormatting /// /// Run TextDecorations. /// - public abstract TextDecorationCollection TextDecorations { get; } + public abstract TextDecorationCollection? TextDecorations { get; } /// /// Brush used to fill text. /// - public abstract IBrush ForegroundBrush { get; } + public abstract IBrush? ForegroundBrush { get; } /// /// Brush used to paint background of run. /// - public abstract IBrush BackgroundBrush { get; } + public abstract IBrush? BackgroundBrush { get; } /// /// Run text culture. /// - public abstract CultureInfo CultureInfo { get; } + public abstract CultureInfo? CultureInfo { get; } /// /// Run vertical box alignment /// public abstract BaselineAlignment BaselineAlignment { get; } - public bool Equals(TextRunProperties other) + public bool Equals(TextRunProperties? other) { if (ReferenceEquals(null, other)) return false; @@ -62,7 +62,7 @@ namespace Avalonia.Media.TextFormatting Equals(CultureInfo, other.CultureInfo); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return ReferenceEquals(this, obj) || obj is TextRunProperties other && Equals(other); } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs index a02ace408f..2892e608ab 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs @@ -46,7 +46,7 @@ namespace Avalonia.Media.TextFormatting /// public GlyphRun ShapeText(ReadOnlySlice text, Typeface typeface, double fontRenderingEmSize, - CultureInfo culture) + CultureInfo? culture) { return _platformImpl.ShapeText(text, typeface, fontRenderingEmSize, culture); } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs index 3c00c49707..4189b24af6 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs @@ -24,8 +24,8 @@ static UnicodeData() { - s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")); - s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")); + s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")!); + s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")!); } /// diff --git a/src/Avalonia.Visuals/Media/Transform.cs b/src/Avalonia.Visuals/Media/Transform.cs index 7cf1b35ada..60701ecfaf 100644 --- a/src/Avalonia.Visuals/Media/Transform.cs +++ b/src/Avalonia.Visuals/Media/Transform.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media /// /// Raised when the transform changes. /// - public event EventHandler Changed; + public event EventHandler? Changed; /// /// Gets the transform's . diff --git a/src/Avalonia.Visuals/Media/TransformConverter.cs b/src/Avalonia.Visuals/Media/TransformConverter.cs index e79c0b8b7b..33c6cd10fb 100644 --- a/src/Avalonia.Visuals/Media/TransformConverter.cs +++ b/src/Avalonia.Visuals/Media/TransformConverter.cs @@ -10,14 +10,14 @@ namespace Avalonia.Media /// public class TransformConverter : TypeConverter { - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { return sourceType == typeof(string); } - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) { - return TransformOperations.Parse((string)value); + return value is string s ? TransformOperations.Parse(s) : null; } } } diff --git a/src/Avalonia.Visuals/Media/TransformGroup.cs b/src/Avalonia.Visuals/Media/TransformGroup.cs index 8e3cfec274..0465efd5a5 100644 --- a/src/Avalonia.Visuals/Media/TransformGroup.cs +++ b/src/Avalonia.Visuals/Media/TransformGroup.cs @@ -24,7 +24,7 @@ namespace Avalonia.Media }; } - private void ChildTransform_Changed(object sender, System.EventArgs e) + private void ChildTransform_Changed(object? sender, System.EventArgs e) { this.RaiseChanged(); } diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs b/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs index 13a24cd523..a8c0fe9b12 100644 --- a/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs +++ b/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs @@ -92,8 +92,8 @@ namespace Avalonia.Media.Transformation } // ReSharper disable PossibleInvalidOperationException - TransformOperation fromValue = fromIdentity ? Identity : from.Value; - TransformOperation toValue = toIdentity ? Identity : to.Value; + TransformOperation fromValue = fromIdentity ? Identity : from!.Value; + TransformOperation toValue = toIdentity ? Identity : to!.Value; // ReSharper restore PossibleInvalidOperationException var interpolationType = toIdentity ? fromValue.Type : toValue.Type; diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs index 17824c3c5e..8245b63440 100644 --- a/src/Avalonia.Visuals/Media/Typeface.cs +++ b/src/Avalonia.Visuals/Media/Typeface.cs @@ -48,7 +48,7 @@ namespace Avalonia.Media /// /// Gets the font family. /// - public FontFamily FontFamily { get; } + public FontFamily? FontFamily { get; } /// /// Gets the font style. @@ -78,7 +78,7 @@ namespace Avalonia.Media return a.Equals(b); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Typeface typeface && Equals(typeface); } diff --git a/src/Avalonia.Visuals/Media/UnicodeRange.cs b/src/Avalonia.Visuals/Media/UnicodeRange.cs index 6179b57ece..0077dd64ac 100644 --- a/src/Avalonia.Visuals/Media/UnicodeRange.cs +++ b/src/Avalonia.Visuals/Media/UnicodeRange.cs @@ -12,7 +12,7 @@ namespace Avalonia.Media public static UnicodeRange Default = Parse("0-10FFFD"); private readonly UnicodeRangeSegment _single; - private readonly IReadOnlyList _segments = null; + private readonly IReadOnlyList? _segments = null; public UnicodeRange(int start, int end) { @@ -37,7 +37,7 @@ namespace Avalonia.Media internal UnicodeRangeSegment Single => _single; - internal IReadOnlyList Segments => _segments; + internal IReadOnlyList? Segments => _segments; /// /// Determines if given value is inside the range. diff --git a/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs b/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs index 57f9c68456..db157304f4 100644 --- a/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs +++ b/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs @@ -6,7 +6,7 @@ namespace Avalonia.Platform public class ExportRenderingSubsystemAttribute : Attribute { public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod, - Type environmentChecker = null) + Type? environmentChecker = null) { Name = name; InitializationType = initializationType; @@ -17,11 +17,11 @@ namespace Avalonia.Platform } public string InitializationMethod { get; private set; } - public Type EnvironmentChecker { get; } + public Type? EnvironmentChecker { get; } public Type InitializationType { get; private set; } public string Name { get; private set; } public int Priority { get; private set; } public OperatingSystemType RequiredOS { get; private set; } - public string RequiresWindowingSubsystem { get; set; } + public string? RequiresWindowingSubsystem { get; set; } } } diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs index ac2c5c9f08..a1f42c171c 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs @@ -55,7 +55,7 @@ namespace Avalonia.Platform /// The fill brush. /// The stroke pen. /// The geometry. - void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry); + void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry); /// /// Draws a rectangle with the specified Brush and Pen. @@ -68,7 +68,7 @@ namespace Avalonia.Platform /// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// - void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, + void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default); /// @@ -81,7 +81,7 @@ namespace Avalonia.Platform /// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// - void DrawEllipse(IBrush brush, IPen pen, Rect rect); + void DrawEllipse(IBrush? brush, IPen? pen, Rect rect); /// /// Draws text. diff --git a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs index e562b45ca8..113c72d373 100644 --- a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs +++ b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs @@ -31,7 +31,7 @@ namespace Avalonia.Platform /// bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, - FontFamily fontFamily, CultureInfo culture, out Typeface typeface); + FontFamily? fontFamily, CultureInfo? culture, out Typeface typeface); /// /// Creates a glyph typeface. diff --git a/src/Avalonia.Visuals/Platform/IGeometryImpl.cs b/src/Avalonia.Visuals/Platform/IGeometryImpl.cs index 79e125c5cb..ed6de1b5c7 100644 --- a/src/Avalonia.Visuals/Platform/IGeometryImpl.cs +++ b/src/Avalonia.Visuals/Platform/IGeometryImpl.cs @@ -23,7 +23,7 @@ namespace Avalonia.Platform /// /// The pen to use. May be null. /// The bounding rectangle. - Rect GetRenderBounds(IPen pen); + Rect GetRenderBounds(IPen? pen); /// /// Indicates whether the geometry's fill contains the specified point. diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index 772f1ac9f3..60ae0b5ef8 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -29,7 +29,7 @@ namespace Avalonia.Platform TextAlignment textAlignment, TextWrapping wrapping, Size constraint, - IReadOnlyList spans); + IReadOnlyList? spans); /// /// Creates an ellipse geometry implementation. diff --git a/src/Avalonia.Visuals/Platform/IRenderTarget.cs b/src/Avalonia.Visuals/Platform/IRenderTarget.cs index 68b3b4c17d..7023f2ca51 100644 --- a/src/Avalonia.Visuals/Platform/IRenderTarget.cs +++ b/src/Avalonia.Visuals/Platform/IRenderTarget.cs @@ -18,7 +18,7 @@ namespace Avalonia.Platform /// A render to be used to render visual brushes. May be null if no visual brushes are /// to be drawn. /// - IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer); + IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer? visualBrushRenderer); } public interface IRenderTargetWithCorruptionInfo : IRenderTarget diff --git a/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs b/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs index d915da2603..73d198d7ef 100644 --- a/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs +++ b/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs @@ -17,6 +17,6 @@ namespace Avalonia.Platform /// The font rendering em size. /// The culture. /// A shaped glyph run. - GlyphRun ShapeText(ReadOnlySlice text, Typeface typeface, double fontRenderingEmSize, CultureInfo culture); + GlyphRun ShapeText(ReadOnlySlice text, Typeface typeface, double fontRenderingEmSize, CultureInfo? culture); } } diff --git a/src/Avalonia.Visuals/Platform/PathGeometryContext.cs b/src/Avalonia.Visuals/Platform/PathGeometryContext.cs index 391a43d1cf..694e9f8d80 100644 --- a/src/Avalonia.Visuals/Platform/PathGeometryContext.cs +++ b/src/Avalonia.Visuals/Platform/PathGeometryContext.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Avalonia.Media; using Avalonia.Platform; @@ -6,8 +7,8 @@ namespace Avalonia.Visuals.Platform { public class PathGeometryContext : IGeometryContext { - private PathFigure _currentFigure; - private PathGeometry _pathGeometry; + private PathFigure? _currentFigure; + private PathGeometry? _pathGeometry; public PathGeometryContext(PathGeometry pathGeometry) { @@ -30,13 +31,16 @@ namespace Avalonia.Visuals.Platform Point = point }; - _currentFigure.Segments.Add(arcSegment); + CurrentFigureSegments().Add(arcSegment); } public void BeginFigure(Point startPoint, bool isFilled) { + ThrowIfDisposed(); + _currentFigure = new PathFigure { StartPoint = startPoint, IsClosed = false, IsFilled = isFilled }; + _pathGeometry.Figures ??= new(); _pathGeometry.Figures.Add(_currentFigure); } @@ -44,14 +48,14 @@ namespace Avalonia.Visuals.Platform { var bezierSegment = new BezierSegment { Point1 = point1, Point2 = point2, Point3 = point3 }; - _currentFigure.Segments.Add(bezierSegment); + CurrentFigureSegments().Add(bezierSegment); } public void QuadraticBezierTo(Point control, Point endPoint) { var quadraticBezierSegment = new QuadraticBezierSegment { Point1 = control, Point2 = endPoint }; - _currentFigure.Segments.Add(quadraticBezierSegment); + CurrentFigureSegments().Add(quadraticBezierSegment); } public void LineTo(Point point) @@ -61,7 +65,7 @@ namespace Avalonia.Visuals.Platform Point = point }; - _currentFigure.Segments.Add(lineSegment); + CurrentFigureSegments().Add(lineSegment); } public void EndFigure(bool isClosed) @@ -76,7 +80,26 @@ namespace Avalonia.Visuals.Platform public void SetFillRule(FillRule fillRule) { + ThrowIfDisposed(); _pathGeometry.FillRule = fillRule; } + + [MemberNotNull(nameof(_pathGeometry))] + private void ThrowIfDisposed() + { + if (_pathGeometry is null) + throw new ObjectDisposedException(nameof(PathGeometryContext)); + } + + private PathSegments CurrentFigureSegments() + { + ThrowIfDisposed(); + + if (_currentFigure is null) + throw new InvalidOperationException("No figure in progress."); + if (_currentFigure.Segments is null) + throw new InvalidOperationException("Current figure's segments cannot be null."); + return _currentFigure.Segments; + } } } diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 6febe9c802..67e7d71fbc 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -211,7 +211,7 @@ namespace Avalonia /// /// True if is a point that equals the current point. /// - public override bool Equals(object obj) => obj is Point other && Equals(other); + public override bool Equals(object? obj) => obj is Point other && Equals(other); /// /// Returns a hash code for a . diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index d2a72db6ae..6d7d6c2e54 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -341,7 +341,7 @@ namespace Avalonia /// /// The object to compare against. /// True if the object is equal to this rectangle; false otherwise. - public override bool Equals(object obj) => obj is Rect other && Equals(other); + public override bool Equals(object? obj) => obj is Rect other && Equals(other); /// /// Returns the hash code for this instance. diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Visuals/RelativePoint.cs index 497820ec65..4550dbd54b 100644 --- a/src/Avalonia.Visuals/RelativePoint.cs +++ b/src/Avalonia.Visuals/RelativePoint.cs @@ -111,7 +111,7 @@ namespace Avalonia /// /// The other object. /// True if the objects are equal, otherwise false. - public override bool Equals(object obj) => obj is RelativePoint other && Equals(other); + public override bool Equals(object? obj) => obj is RelativePoint other && Equals(other); /// /// Checks if the equals another point. diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Visuals/RelativeRect.cs index 1d1f53e299..f6eb257c85 100644 --- a/src/Avalonia.Visuals/RelativeRect.cs +++ b/src/Avalonia.Visuals/RelativeRect.cs @@ -113,7 +113,7 @@ namespace Avalonia /// /// The other object. /// True if the objects are equal, otherwise false. - public override bool Equals(object obj) => obj is RelativeRect other && Equals(other); + public override bool Equals(object? obj) => obj is RelativeRect other && Equals(other); /// /// Checks if the equals another rectangle. diff --git a/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs index 8d6c3e67c1..43e00f3215 100644 --- a/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs +++ b/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs @@ -14,10 +14,10 @@ namespace Avalonia.Rendering /// public class DefaultRenderTimer : IRenderTimer { - private IRuntimePlatform _runtime; + private IRuntimePlatform? _runtime; private int _subscriberCount; - private Action _tick; - private IDisposable _subscription; + private Action? _tick; + private IDisposable? _subscription; /// /// Initializes a new instance of the class. @@ -77,10 +77,8 @@ namespace Avalonia.Rendering /// protected virtual IDisposable StartCore(Action tick) { - if (_runtime == null) - { - _runtime = AvaloniaLocator.Current.GetService(); - } + _runtime ??= AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IRuntimePlatform."); return _runtime.StartSystemTimer( TimeSpan.FromSeconds(1.0 / FramesPerSecond), @@ -92,13 +90,13 @@ namespace Avalonia.Rendering /// protected void Stop() { - _subscription.Dispose(); + _subscription?.Dispose(); _subscription = null; } private void InternalTick(TimeSpan tickCount) { - _tick(tickCount); + _tick?.Invoke(tickCount); } } } diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index fe63fdec46..7b9c515b97 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Avalonia.Logging; @@ -19,20 +20,20 @@ namespace Avalonia.Rendering /// public class DeferredRenderer : RendererBase, IRenderer, IRenderLoopTask, IVisualBrushRenderer { - private readonly IDispatcher _dispatcher; - private readonly IRenderLoop _renderLoop; + private readonly IDispatcher? _dispatcher; + private readonly IRenderLoop? _renderLoop; private readonly IVisual _root; private readonly ISceneBuilder _sceneBuilder; private bool _running; private bool _disposed; - private volatile IRef _scene; - private DirtyVisuals _dirty; - private HashSet _recalculateChildren; - private IRef _overlay; + private volatile IRef? _scene; + private DirtyVisuals? _dirty; + private HashSet? _recalculateChildren; + private IRef? _overlay; private int _lastSceneId = -1; private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects(); - private IRef _currentDraw; + private IRef? _currentDraw; private readonly IDeferredRendererLock _lock; private readonly object _sceneLock = new object(); private readonly object _startStopLock = new object(); @@ -50,14 +51,12 @@ namespace Avalonia.Rendering public DeferredRenderer( IRenderRoot root, IRenderLoop renderLoop, - ISceneBuilder sceneBuilder = null, - IDispatcher dispatcher = null, - IDeferredRendererLock rendererLock = null) : base(true) + ISceneBuilder? sceneBuilder = null, + IDispatcher? dispatcher = null, + IDeferredRendererLock? rendererLock = null) : base(true) { - Contract.Requires(root != null); - _dispatcher = dispatcher ?? Dispatcher.UIThread; - _root = root; + _root = root ?? throw new ArgumentNullException(nameof(root)); _sceneBuilder = sceneBuilder ?? new SceneBuilder(); Layers = new RenderLayers(); _renderLoop = renderLoop; @@ -77,13 +76,10 @@ namespace Avalonia.Rendering public DeferredRenderer( IVisual root, IRenderTarget renderTarget, - ISceneBuilder sceneBuilder = null) : base(true) + ISceneBuilder? sceneBuilder = null) : base(true) { - Contract.Requires(root != null); - Contract.Requires(renderTarget != null); - - _root = root; - RenderTarget = renderTarget; + _root = root ?? throw new ArgumentNullException(nameof(root)); + RenderTarget = renderTarget ?? throw new ArgumentNullException(nameof(renderTarget)); _sceneBuilder = sceneBuilder ?? new SceneBuilder(); Layers = new RenderLayers(); _lock = new ManagedDeferredRendererLock(); @@ -99,7 +95,7 @@ namespace Avalonia.Rendering /// /// Gets or sets a path to which rendered frame should be rendered for debugging. /// - public string DebugFramesPath { get; set; } + public string? DebugFramesPath { get; set; } /// /// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered @@ -107,7 +103,7 @@ namespace Avalonia.Rendering public bool RenderOnlyOnRenderThread { get; set; } /// - public event EventHandler SceneInvalidated; + public event EventHandler? SceneInvalidated; /// /// Gets the render layers. @@ -117,7 +113,7 @@ namespace Avalonia.Rendering /// /// Gets the current render target. /// - internal IRenderTarget RenderTarget { get; private set; } + internal IRenderTarget? RenderTarget { get; private set; } /// public void AddDirty(IVisual visual) @@ -167,7 +163,7 @@ namespace Avalonia.Rendering } /// - public IEnumerable HitTest(Point p, IVisual root, Func filter) + public IEnumerable HitTest(Point p, IVisual root, Func? filter) { EnsureCanHitTest(); @@ -177,7 +173,7 @@ namespace Avalonia.Rendering } /// - public IVisual HitTestFirst(Point p, IVisual root, Func filter) + public IVisual? HitTestFirst(Point p, IVisual root, Func? filter) { EnsureCanHitTest(); @@ -199,7 +195,7 @@ namespace Avalonia.Rendering while (true) { - Scene scene; + Scene? scene; bool? updated; lock (_sceneLock) { @@ -297,7 +293,7 @@ namespace Avalonia.Rendering internal void UnitTestRender() => Render(false); - internal Scene UnitTestScene() => _scene.Item; + internal Scene? UnitTestScene() => _scene?.Item; private void EnsureCanHitTest() { @@ -315,7 +311,7 @@ namespace Avalonia.Rendering if (l == null) return; - IDrawingContextImpl context = null; + IDrawingContextImpl? context = null; try { try @@ -358,10 +354,10 @@ namespace Avalonia.Rendering } } - private (IRef scene, bool updated) UpdateRenderLayersAndConsumeSceneIfNeeded(ref IDrawingContextImpl context, + private (IRef? scene, bool updated) UpdateRenderLayersAndConsumeSceneIfNeeded(ref IDrawingContextImpl? context, bool recursiveCall = false) { - IRef sceneRef; + IRef? sceneRef; lock (_sceneLock) sceneRef = _scene?.Clone(); if (sceneRef == null) @@ -416,7 +412,7 @@ namespace Avalonia.Rendering } - private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds) + private void Render(IDrawingContextImpl context, VisualNode node, IVisual? layer, Rect clipBounds) { if (layer == null || node.LayerRoot == layer) { @@ -459,7 +455,7 @@ namespace Avalonia.Rendering if (layer.Dirty.IsEmpty && !renderLayer.IsEmpty) continue; var renderTarget = renderLayer.Bitmap; - var node = (VisualNode)scene.FindNode(layer.LayerRoot); + var node = (VisualNode?)scene.FindNode(layer.LayerRoot); if (node != null) { @@ -515,7 +511,7 @@ namespace Avalonia.Rendering Math.Ceiling(rect.Bottom * scale) / scale)); } - private void RenderOverlay(Scene scene, ref IDrawingContextImpl parentContent) + private void RenderOverlay(Scene scene, ref IDrawingContextImpl? parentContent) { EnsureDrawingContext(ref parentContent); @@ -545,7 +541,7 @@ namespace Avalonia.Rendering } } - private void RenderComposite(Scene scene, ref IDrawingContextImpl context) + private void RenderComposite(Scene scene, ref IDrawingContextImpl? context) { EnsureDrawingContext(ref context); @@ -596,7 +592,7 @@ namespace Avalonia.Rendering } } - private void EnsureDrawingContext(ref IDrawingContextImpl context) + private void EnsureDrawingContext([NotNull] ref IDrawingContextImpl? context) { if (context != null) { @@ -605,7 +601,7 @@ namespace Avalonia.Rendering if ((RenderTarget as IRenderTargetWithCorruptionInfo)?.IsCorrupted == true) { - RenderTarget.Dispose(); + RenderTarget!.Dispose(); RenderTarget = null; } @@ -647,10 +643,10 @@ namespace Avalonia.Rendering } else { - foreach (var visual in _recalculateChildren) + foreach (var visual in _recalculateChildren!) { var node = scene.FindNode(visual); - ((VisualNode)node)?.SortChildren(scene); + ((VisualNode?)node)?.SortChildren(scene); } _recalculateChildren.Clear(); @@ -724,7 +720,7 @@ namespace Avalonia.Rendering foreach (var layer in Layers) { - var fileName = Path.Combine(DebugFramesPath, $"frame-{id}-layer-{index++}.png"); + var fileName = Path.Combine(DebugFramesPath ?? string.Empty, $"frame-{id}-layer-{index++}.png"); layer.Bitmap.Item.Save(fileName); } } diff --git a/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs index b2ed3afe6a..eab3dca58e 100644 --- a/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs +++ b/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs @@ -4,6 +4,6 @@ namespace Avalonia.Rendering { public interface IDeferredRendererLock { - IDisposable TryLock(); + IDisposable? TryLock(); } } diff --git a/src/Avalonia.Visuals/Rendering/IRenderer.cs b/src/Avalonia.Visuals/Rendering/IRenderer.cs index 0c7440d159..e998f78d5c 100644 --- a/src/Avalonia.Visuals/Rendering/IRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/IRenderer.cs @@ -27,7 +27,7 @@ namespace Avalonia.Rendering /// Indicates that the underlying low-level scene information has been updated. Used to /// signal that an update to the current pointer-over state may be required. /// - event EventHandler SceneInvalidated; + event EventHandler? SceneInvalidated; /// /// Mark a visual as dirty and needing re-rendering. @@ -57,7 +57,7 @@ namespace Avalonia.Rendering /// children will be excluded from the results. /// /// The visual at the specified point, topmost first. - IVisual HitTestFirst(Point p, IVisual root, Func filter); + IVisual? HitTestFirst(Point p, IVisual root, Func filter); /// /// Informs the renderer that the z-ordering of a visual's children has changed. diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 88fbd290e6..f6642102f7 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -19,9 +19,9 @@ namespace Avalonia.Rendering public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer { private readonly IVisual _root; - private readonly IRenderRoot _renderRoot; + private readonly IRenderRoot? _renderRoot; private bool _updateTransformedBounds = true; - private IRenderTarget _renderTarget; + private IRenderTarget? _renderTarget; /// /// Initializes a new instance of the class. @@ -29,9 +29,7 @@ namespace Avalonia.Rendering /// The control to render. public ImmediateRenderer(IVisual root) { - Contract.Requires(root != null); - - _root = root; + _root = root ?? throw new ArgumentNullException(nameof(root)); _renderRoot = root as IRenderRoot; } @@ -49,7 +47,7 @@ namespace Avalonia.Rendering public bool DrawDirtyRects { get; set; } /// - public event EventHandler SceneInvalidated; + public event EventHandler? SceneInvalidated; /// public void Paint(Rect rect) @@ -169,7 +167,7 @@ namespace Avalonia.Rendering return HitTest(root, p, filter); } - public IVisual HitTestFirst(Point p, IVisual root, Func filter) + public IVisual? HitTestFirst(Point p, IVisual root, Func filter) { return HitTest(root, p, filter).FirstOrDefault(); } @@ -233,9 +231,9 @@ namespace Avalonia.Rendering private static IEnumerable HitTest( IVisual visual, Point p, - Func filter) + Func? filter) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); if (filter?.Invoke(visual) != false) { diff --git a/src/Avalonia.Visuals/Rendering/RenderLayers.cs b/src/Avalonia.Visuals/Rendering/RenderLayers.cs index e82934fbad..7583886835 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLayers.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLayers.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.VisualTree; @@ -19,9 +20,8 @@ namespace Avalonia.Rendering for (var i = scene.Layers.Count - 1; i >= 0; --i) { var src = scene.Layers[i]; - RenderLayer layer; - if (!_index.TryGetValue(src.LayerRoot, out layer)) + if (!_index.TryGetValue(src.LayerRoot, out var layer)) { layer = new RenderLayer(context, scene.Size, scene.Scaling, src.LayerRoot); _inner.Add(layer); @@ -59,7 +59,7 @@ namespace Avalonia.Rendering _inner.Clear(); } - public bool TryGetValue(IVisual layerRoot, out RenderLayer value) + public bool TryGetValue(IVisual layerRoot, [NotNullWhen(true)] out RenderLayer? value) { return _index.TryGetValue(layerRoot, out value); } diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index b2a6b0da19..a5d7e15f93 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -19,7 +19,7 @@ namespace Avalonia.Rendering private readonly IDispatcher _dispatcher; private List _items = new List(); private List _itemsCopy = new List(); - private IRenderTimer _timer; + private IRenderTimer? _timer; private int _inTick; private int _inUpdate; @@ -49,19 +49,15 @@ namespace Avalonia.Rendering { get { - if (_timer == null) - { - _timer = AvaloniaLocator.Current.GetService(); - } - - return _timer; + return _timer ??= AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Cannot locate IRenderTimer."); } } /// public void Add(IRenderLoopTask i) { - Contract.Requires(i != null); + _ = i ?? throw new ArgumentNullException(nameof(i)); Dispatcher.UIThread.VerifyAccess(); lock (_items) @@ -78,7 +74,7 @@ namespace Avalonia.Rendering /// public void Remove(IRenderLoopTask i) { - Contract.Requires(i != null); + _ = i ?? throw new ArgumentNullException(nameof(i)); Dispatcher.UIThread.VerifyAccess(); lock (_items) { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs index c559f05d70..cd3dac699a 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs @@ -17,6 +17,6 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets a collection of child scenes that are needed to draw visual brushes. /// - public abstract IDictionary ChildScenes { get; } + public abstract IDictionary? ChildScenes { get; } } } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index da1a00504a..688cbd83c8 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -14,7 +14,7 @@ namespace Avalonia.Rendering.SceneGraph internal class DeferredDrawingContextImpl : IDrawingContextImpl, IDrawingContextWithAcrylicLikeSupport { private readonly ISceneBuilder _sceneBuilder; - private VisualNode _node; + private VisualNode? _node; private int _childIndex; private int _drawOperationindex; @@ -48,7 +48,7 @@ namespace Avalonia.Rendering.SceneGraph /// public UpdateState BeginUpdate(VisualNode node) { - Contract.Requires(node != null); + _ = node ?? throw new ArgumentNullException(nameof(node)); if (_node != null) { @@ -93,11 +93,11 @@ namespace Avalonia.Rendering.SceneGraph /// public void TrimChildren() { - _node.TrimChildren(_childIndex); + _node!.TrimChildren(_childIndex); } /// - public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) + public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry) { var next = NextDrawAs(); @@ -149,7 +149,7 @@ namespace Avalonia.Rendering.SceneGraph } /// - public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, + public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default) { var next = NextDrawAs(); @@ -179,7 +179,7 @@ namespace Avalonia.Rendering.SceneGraph } } - public void DrawEllipse(IBrush brush, IPen pen, Rect rect) + public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect) { var next = NextDrawAs(); @@ -259,7 +259,7 @@ namespace Avalonia.Rendering.SceneGraph if (next == null || !next.Item.Equals(null)) { - Add((new GeometryClipNode())); + Add(new GeometryClipNode()); } else { @@ -343,8 +343,11 @@ namespace Avalonia.Rendering.SceneGraph } /// - public void PushGeometryClip(IGeometryImpl clip) + public void PushGeometryClip(IGeometryImpl? clip) { + if (clip is null) + return; + var next = NextDrawAs(); if (next == null || !next.Item.Equals(Transform, clip)) @@ -406,7 +409,7 @@ namespace Avalonia.Rendering.SceneGraph { public UpdateState( DeferredDrawingContextImpl owner, - VisualNode node, + VisualNode? node, int childIndex, int drawOperationIndex) { @@ -418,9 +421,9 @@ namespace Avalonia.Rendering.SceneGraph public void Dispose() { - Owner._node.TrimDrawOperations(Owner._drawOperationindex); + Owner._node!.TrimDrawOperations(Owner._drawOperationindex); - var dirty = Owner.Layers.GetOrAdd(Owner._node.LayerRoot).Dirty; + var dirty = Owner.Layers.GetOrAdd(Owner._node.LayerRoot!).Dirty; var drawOperations = Owner._node.DrawOperations; var drawOperationsCount = drawOperations.Count; @@ -436,7 +439,7 @@ namespace Avalonia.Rendering.SceneGraph } public DeferredDrawingContextImpl Owner { get; } - public VisualNode Node { get; } + public VisualNode? Node { get; } public int ChildIndex { get; } public int DrawOperationIndex { get; } } @@ -451,7 +454,7 @@ namespace Avalonia.Rendering.SceneGraph private void Add(IRef node) { - if (_drawOperationindex < _node.DrawOperations.Count) + if (_drawOperationindex < _node!.DrawOperations.Count) { _node.ReplaceDrawOperation(_drawOperationindex, node); } @@ -463,12 +466,12 @@ namespace Avalonia.Rendering.SceneGraph ++_drawOperationindex; } - private IRef NextDrawAs() where T : class, IDrawOperation + private IRef? NextDrawAs() where T : class, IDrawOperation { - return _drawOperationindex < _node.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as IRef : null; + return _drawOperationindex < _node!.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as IRef : null; } - private IDictionary CreateChildScene(IBrush brush) + private IDictionary? CreateChildScene(IBrush? brush) { var visualBrush = brush as VisualBrush; diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs index a8c5579a4b..504256b932 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs @@ -14,10 +14,10 @@ namespace Avalonia.Rendering.SceneGraph { public EllipseNode( Matrix transform, - IBrush brush, - IPen pen, + IBrush? brush, + IPen? pen, Rect rect, - IDictionary childScenes = null) + IDictionary? childScenes = null) : base(rect.Inflate(pen?.Thickness ?? 0), transform) { Transform = transform; @@ -30,12 +30,12 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the fill brush. /// - public IBrush Brush { get; } + public IBrush? Brush { get; } /// /// Gets the stroke pen. /// - public ImmutablePen Pen { get; } + public ImmutablePen? Pen { get; } /// /// Gets the transform with which the node will be drawn. @@ -47,9 +47,9 @@ namespace Avalonia.Rendering.SceneGraph /// public Rect Rect { get; } - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } - public bool Equals(Matrix transform, IBrush brush, IPen pen, Rect rect) + public bool Equals(Matrix transform, IBrush? brush, IPen? pen, Rect rect) { return transform == Transform && Equals(brush, Brush) && diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs index 8bd079d070..59ebcf5109 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs @@ -23,7 +23,7 @@ namespace Avalonia.Rendering.SceneGraph : base(rect.Rect, transform) { Transform = transform; - Material = material?.ToImmutable(); + Material = material.ToImmutable(); Rect = rect; } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs index 16092d4cbb..667b66420b 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs @@ -33,7 +33,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the clip to be pushed or null if the operation represents a pop. /// - public IGeometryImpl Clip { get; } + public IGeometryImpl? Clip { get; } /// /// Gets the transform with which the node will be drawn. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs index 508ca0ad18..7de1035441 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs @@ -20,10 +20,10 @@ namespace Avalonia.Rendering.SceneGraph /// The geometry. /// Child scenes for drawing visual brushes. public GeometryNode(Matrix transform, - IBrush brush, - IPen pen, + IBrush? brush, + IPen? pen, IGeometryImpl geometry, - IDictionary childScenes = null) + IDictionary? childScenes = null) : base(geometry.GetRenderBounds(pen), transform) { Transform = transform; @@ -41,12 +41,12 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the fill brush. /// - public IBrush Brush { get; } + public IBrush? Brush { get; } /// /// Gets the stroke pen. /// - public ImmutablePen Pen { get; } + public ImmutablePen? Pen { get; } /// /// Gets the geometry to draw. @@ -54,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph public IGeometryImpl Geometry { get; } /// - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } /// /// Determines if this draw operation equals another. @@ -68,7 +68,7 @@ namespace Avalonia.Rendering.SceneGraph /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(Matrix transform, IBrush brush, IPen pen, IGeometryImpl geometry) + public bool Equals(Matrix transform, IBrush? brush, IPen? pen, IGeometryImpl geometry) { return transform == Transform && Equals(brush, Brush) && diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs index a6dba1bd32..d6da087120 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs @@ -23,11 +23,11 @@ namespace Avalonia.Rendering.SceneGraph Matrix transform, IBrush foreground, GlyphRun glyphRun, - IDictionary childScenes = null) + IDictionary? childScenes = null) : base(new Rect(glyphRun.Size), transform) { Transform = transform; - Foreground = foreground?.ToImmutable(); + Foreground = foreground.ToImmutable(); GlyphRun = glyphRun; ChildScenes = childScenes; } @@ -48,7 +48,7 @@ namespace Avalonia.Rendering.SceneGraph public GlyphRun GlyphRun { get; } /// - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } /// public override void Render(IDrawingContextImpl context) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs index 6d12b5bca4..e042236346 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs @@ -19,7 +19,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the parent scene graph node. /// - IVisualNode Parent { get; } + IVisualNode? Parent { get; } /// /// Gets the transform for the node from global to control coordinates. @@ -54,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the node's clip geometry, if any. /// - IGeometryImpl GeometryClip { get; set; } + IGeometryImpl? GeometryClip { get; set; } /// /// Gets a value indicating whether one of the node's ancestors has a geometry clip. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs index 3dc6d5f50e..a9e1ce8ed7 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs @@ -25,11 +25,11 @@ namespace Avalonia.Rendering.SceneGraph IPen pen, Point p1, Point p2, - IDictionary childScenes = null) + IDictionary? childScenes = null) : base(LineBoundsHelper.CalculateBounds(p1, p2, pen), transform) { Transform = transform; - Pen = pen?.ToImmutable(); + Pen = pen.ToImmutable(); P1 = p1; P2 = p2; ChildScenes = childScenes; @@ -56,7 +56,7 @@ namespace Avalonia.Rendering.SceneGraph public Point P2 { get; } /// - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } /// /// Determines if this draw operation equals another. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs index b8e7b150ac..4b6e7d2254 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs @@ -17,10 +17,10 @@ namespace Avalonia.Rendering.SceneGraph /// The opacity mask to push. /// The bounds of the mask. /// Child scenes for drawing visual brushes. - public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary childScenes = null) + public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary? childScenes = null) : base(Rect.Empty, Matrix.Identity) { - Mask = mask?.ToImmutable(); + Mask = mask.ToImmutable(); MaskBounds = bounds; ChildScenes = childScenes; } @@ -37,7 +37,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the mask to be pushed or null if the operation represents a pop. /// - public IBrush Mask { get; } + public IBrush? Mask { get; } /// /// Gets the bounds of the opacity mask or null if the operation represents a pop. @@ -45,7 +45,7 @@ namespace Avalonia.Rendering.SceneGraph public Rect? MaskBounds { get; } /// - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } /// public override bool HitTest(Point p) => false; @@ -60,14 +60,14 @@ namespace Avalonia.Rendering.SceneGraph /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(IBrush mask, Rect? bounds) => Mask == mask && MaskBounds == bounds; + public bool Equals(IBrush? mask, Rect? bounds) => Mask == mask && MaskBounds == bounds; /// public override void Render(IDrawingContextImpl context) { if (Mask != null) { - context.PushOpacityMask(Mask, MaskBounds.Value); + context.PushOpacityMask(Mask, MaskBounds!.Value); } else { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs index 285fbce605..3279c3a549 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs @@ -22,11 +22,11 @@ namespace Avalonia.Rendering.SceneGraph /// Child scenes for drawing visual brushes. public RectangleNode( Matrix transform, - IBrush brush, - IPen pen, + IBrush? brush, + IPen? pen, RoundedRect rect, BoxShadows boxShadows, - IDictionary childScenes = null) + IDictionary? childScenes = null) : base(boxShadows.TransformBounds(rect.Rect).Inflate((pen?.Thickness ?? 0) / 2), transform) { Transform = transform; @@ -45,12 +45,12 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the fill brush. /// - public IBrush Brush { get; } + public IBrush? Brush { get; } /// /// Gets the stroke pen. /// - public ImmutablePen Pen { get; } + public ImmutablePen? Pen { get; } /// /// Gets the rectangle to draw. @@ -63,7 +63,7 @@ namespace Avalonia.Rendering.SceneGraph public BoxShadows BoxShadows { get; } /// - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } /// /// Determines if this draw operation equals another. @@ -78,7 +78,7 @@ namespace Avalonia.Rendering.SceneGraph /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(Matrix transform, IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadows) + public bool Equals(Matrix transform, IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows) { return transform == Transform && Equals(brush, Brush) && diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs index d8e5baac97..20c23d7bee 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs @@ -32,7 +32,7 @@ namespace Avalonia.Rendering.SceneGraph private Scene(VisualNode root, Dictionary index, SceneLayers layers, int generation) { - Contract.Requires(root != null); + _ = root ?? throw new ArgumentNullException(nameof(root)); var renderRoot = root.Visual as IRenderRoot; @@ -76,7 +76,7 @@ namespace Avalonia.Rendering.SceneGraph /// The node. public void Add(IVisualNode node) { - Contract.Requires(node != null); + _ = node ?? throw new ArgumentNullException(nameof(node)); _index.Add(node.Visual, node); } @@ -115,10 +115,9 @@ namespace Avalonia.Rendering.SceneGraph /// /// The node representing the visual or null if it could not be found. /// - public IVisualNode FindNode(IVisual visual) + public IVisualNode? FindNode(IVisual visual) { - IVisualNode node; - _index.TryGetValue(visual, out node); + _index.TryGetValue(visual, out var node); return node; } @@ -129,7 +128,7 @@ namespace Avalonia.Rendering.SceneGraph /// The root of the subtree to search. /// A filter. May be null. /// The visuals at the specified point. - public IEnumerable HitTest(Point p, IVisual root, Func filter) + public IEnumerable HitTest(Point p, IVisual root, Func? filter) { var node = FindNode(root); return (node != null) ? new HitTestEnumerable(node, filter, p, Root) : Enumerable.Empty(); @@ -142,7 +141,7 @@ namespace Avalonia.Rendering.SceneGraph /// The root of the subtree to search. /// A filter. May be null. /// The visual at the specified point. - public IVisual HitTestFirst(Point p, IVisual root, Func filter) + public IVisual? HitTestFirst(Point p, IVisual root, Func? filter) { var node = FindNode(root); return (node != null) ? HitTestFirst(node, p, filter) : null; @@ -154,14 +153,14 @@ namespace Avalonia.Rendering.SceneGraph /// The node. public void Remove(IVisualNode node) { - Contract.Requires(node != null); + _ = node ?? throw new ArgumentNullException(nameof(node)); _index.Remove(node.Visual); node.Dispose(); } - private VisualNode Clone(VisualNode source, IVisualNode parent, Dictionary index) + private VisualNode Clone(VisualNode source, IVisualNode? parent, Dictionary index) { var result = source.Clone(parent); @@ -185,7 +184,7 @@ namespace Avalonia.Rendering.SceneGraph return result; } - private IVisual HitTestFirst(IVisualNode root, Point p, Func filter) + private IVisual HitTestFirst(IVisualNode root, Point p, Func? filter) { using var enumerator = new HitTestEnumerator(root, filter, p, Root); @@ -197,11 +196,11 @@ namespace Avalonia.Rendering.SceneGraph private class HitTestEnumerable : IEnumerable { private readonly IVisualNode _root; - private readonly Func _filter; + private readonly Func? _filter; private readonly IVisualNode _sceneRoot; private readonly Point _point; - public HitTestEnumerable(IVisualNode root, Func filter, Point point, IVisualNode sceneRoot) + public HitTestEnumerable(IVisualNode root, Func? filter, Point point, IVisualNode sceneRoot) { _root = root; _filter = filter; @@ -223,12 +222,12 @@ namespace Avalonia.Rendering.SceneGraph private struct HitTestEnumerator : IEnumerator { private readonly PooledStack _nodeStack; - private readonly Func _filter; + private readonly Func? _filter; private readonly IVisualNode _sceneRoot; - private IVisual _current; + private IVisual? _current; private readonly Point _point; - public HitTestEnumerator(IVisualNode root, Func filter, Point point, IVisualNode sceneRoot) + public HitTestEnumerator(IVisualNode root, Func? filter, Point point, IVisualNode sceneRoot) { _nodeStack = new PooledStack(); _nodeStack.Push(new Entry(root, false, null, true)); @@ -283,7 +282,7 @@ namespace Avalonia.Rendering.SceneGraph throw new NotSupportedException(); } - public IVisual Current => _current; + public IVisual Current => _current!; object IEnumerator.Current => Current; @@ -307,7 +306,7 @@ namespace Avalonia.Rendering.SceneGraph if (node.GeometryClip != null) { var controlPoint = _sceneRoot.Visual.TranslatePoint(_point, node.Visual); - clipped = !node.GeometryClip.FillContains(controlPoint.Value); + clipped = !node.GeometryClip.FillContains(controlPoint!.Value); } if (!clipped && node.Visual is ICustomHitTest custom) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index b9131c26f4..cb916293ac 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -15,7 +15,7 @@ namespace Avalonia.Rendering.SceneGraph /// public void UpdateAll(Scene scene) { - Contract.Requires(scene != null); + _ = scene ?? throw new ArgumentNullException(nameof(scene)); Dispatcher.UIThread.VerifyAccess(); UpdateSize(scene); @@ -32,8 +32,8 @@ namespace Avalonia.Rendering.SceneGraph /// public bool Update(Scene scene, IVisual visual) { - Contract.Requires(scene != null); - Contract.Requires(visual != null); + _ = scene ?? throw new ArgumentNullException(nameof(scene)); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); Dispatcher.UIThread.VerifyAccess(); @@ -42,7 +42,7 @@ namespace Avalonia.Rendering.SceneGraph throw new AvaloniaInternalException("Cannot update the scene for an invisible root visual."); } - var node = (VisualNode)scene.FindNode(visual); + var node = (VisualNode?)scene.FindNode(visual); if (visual == scene.Root.Visual) { @@ -58,7 +58,7 @@ namespace Avalonia.Rendering.SceneGraph // The control has changed parents. Remove the node and recurse into the new parent node. ((VisualNode)node.Parent).RemoveChild(node); Deindex(scene, node); - node = (VisualNode)scene.FindNode(visual.VisualParent); + node = (VisualNode?)scene.FindNode(visual.VisualParent); } if (visual.IsVisible) @@ -101,7 +101,7 @@ namespace Avalonia.Rendering.SceneGraph { // The control has been hidden so remove it from its parent and deindex the // node and its descendents. - ((VisualNode)node.Parent)?.RemoveChild(node); + ((VisualNode?)node.Parent)?.RemoveChild(node); Deindex(scene, node); return true; } @@ -112,7 +112,7 @@ namespace Avalonia.Rendering.SceneGraph // The control has been removed so remove it from its parent and deindex the // node and its descendents. var trim = FindFirstDeadAncestor(scene, node); - ((VisualNode)trim.Parent).RemoveChild(trim); + ((VisualNode)trim.Parent!).RemoveChild(trim); Deindex(scene, trim); return true; } @@ -120,24 +120,24 @@ namespace Avalonia.Rendering.SceneGraph return false; } - private static VisualNode FindExistingAncestor(Scene scene, IVisual visual) + private static VisualNode? FindExistingAncestor(Scene scene, IVisual visual) { var node = scene.FindNode(visual); while (node == null && visual.IsVisible) { - visual = visual.VisualParent; + visual = visual.VisualParent!; node = scene.FindNode(visual); } - return visual.IsVisible ? (VisualNode)node : null; + return visual.IsVisible ? (VisualNode?)node : null; } private static VisualNode FindFirstDeadAncestor(Scene scene, IVisualNode node) { var parent = node.Parent; - while (parent.Visual.VisualRoot == null) + while (parent!.Visual.VisualRoot == null) { node = parent; parent = node.Parent; @@ -148,7 +148,7 @@ namespace Avalonia.Rendering.SceneGraph private static object GetOrCreateChildNode(Scene scene, IVisual child, VisualNode parent) { - var result = (VisualNode)scene.FindNode(child); + var result = (VisualNode?)scene.FindNode(child); if (result != null && result.Parent != parent) { @@ -173,7 +173,7 @@ namespace Avalonia.Rendering.SceneGraph var bounds = new Rect(visual.Bounds.Size); var contextImpl = (DeferredDrawingContextImpl)context.PlatformImpl; - contextImpl.Layers.Find(node.LayerRoot)?.Dirty.Add(node.Bounds); + contextImpl.Layers.Find(node.LayerRoot!)?.Dirty.Add(node.Bounds); if (visual.IsVisible) { @@ -353,7 +353,7 @@ namespace Avalonia.Rendering.SceneGraph node.SubTreeUpdated = true; - scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds); + scene.Layers[node.LayerRoot!].Dirty.Add(node.Bounds); node.Visual.TransformedBounds = null; @@ -365,10 +365,10 @@ namespace Avalonia.Rendering.SceneGraph private static void ClearLayer(Scene scene, VisualNode node) { - var parent = (VisualNode)node.Parent; + var parent = (VisualNode)node.Parent!; var oldLayerRoot = node.LayerRoot; - var newLayerRoot = parent.LayerRoot; - var existingDirtyRects = scene.Layers[node.LayerRoot].Dirty; + var newLayerRoot = parent.LayerRoot!; + var existingDirtyRects = scene.Layers[node.LayerRoot!].Dirty; var newDirtyRects = scene.Layers[newLayerRoot].Dirty; existingDirtyRects.Coalesce(); @@ -378,16 +378,16 @@ namespace Avalonia.Rendering.SceneGraph newDirtyRects.Add(r); } - var oldLayer = scene.Layers[oldLayerRoot]; + var oldLayer = scene.Layers[oldLayerRoot!]; PropagateLayer(node, scene.Layers[newLayerRoot], oldLayer); scene.Layers.Remove(oldLayer); } private static void MakeLayer(Scene scene, VisualNode node) { - var oldLayerRoot = node.LayerRoot; + var oldLayerRoot = node.LayerRoot!; var layer = scene.Layers.Add(node.Visual); - var oldLayer = scene.Layers[oldLayerRoot]; + var oldLayer = scene.Layers[oldLayerRoot!]; UpdateLayer(node, layer); PropagateLayer(node, layer, scene.Layers[oldLayerRoot]); @@ -433,22 +433,23 @@ namespace Avalonia.Rendering.SceneGraph // HACK: Disabled layers because they're broken in current renderer. See #2244. private static bool ShouldStartLayer(IVisual visual) => false; - private static IGeometryImpl CreateLayerGeometryClip(VisualNode node) + private static IGeometryImpl? CreateLayerGeometryClip(VisualNode node) { - IGeometryImpl result = null; + IGeometryImpl? result = null; + VisualNode? n = node; for (;;) { - node = (VisualNode)node.Parent; + n = (VisualNode?)n!.Parent; - if (node == null || (node.GeometryClip == null && !node.HasAncestorGeometryClip)) + if (n == null || (n.GeometryClip == null && !n.HasAncestorGeometryClip)) { break; } - if (node?.GeometryClip != null) + if (n?.GeometryClip != null) { - var transformed = node.GeometryClip.WithTransform(node.Transform); + var transformed = n.GeometryClip.WithTransform(n.Transform); result = result == null ? transformed : result.Intersect(transformed); } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs index 02fae562ef..e9474f6e98 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs @@ -54,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets or sets the opacity mask for the layer. /// - public IBrush OpacityMask { get; set; } + public IBrush? OpacityMask { get; set; } /// /// Gets or sets the target rectangle for the layer opacity mask. @@ -64,7 +64,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the layer's geometry clip. /// - public IGeometryImpl GeometryClip { get; set; } + public IGeometryImpl? GeometryClip { get; set; } /// /// Gets the dirty rectangles for the layer. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs index 25f7383a1a..16d704e5f6 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs @@ -80,7 +80,7 @@ namespace Avalonia.Rendering.SceneGraph /// The created layer. public SceneLayer Add(IVisual layerRoot) { - Contract.Requires(layerRoot != null); + _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot)); var distance = layerRoot.CalculateDistanceFromAncestor(_root); var layer = new SceneLayer(layerRoot, distance); @@ -117,7 +117,7 @@ namespace Avalonia.Rendering.SceneGraph /// public bool Exists(IVisual layerRoot) { - Contract.Requires(layerRoot != null); + _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot)); return _index.ContainsKey(layerRoot); } @@ -127,10 +127,9 @@ namespace Avalonia.Rendering.SceneGraph /// /// The root visual. /// The layer if found, otherwise null. - public SceneLayer Find(IVisual layerRoot) + public SceneLayer? Find(IVisual layerRoot) { - SceneLayer result; - _index.TryGetValue(layerRoot, out result); + _index.TryGetValue(layerRoot, out var result); return result; } @@ -141,11 +140,9 @@ namespace Avalonia.Rendering.SceneGraph /// The layer. public SceneLayer GetOrAdd(IVisual layerRoot) { - Contract.Requires(layerRoot != null); + _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot)); - SceneLayer result; - - if (!_index.TryGetValue(layerRoot, out result)) + if (!_index.TryGetValue(layerRoot, out var result)) { result = Add(layerRoot); } @@ -160,11 +157,9 @@ namespace Avalonia.Rendering.SceneGraph /// True if a matching layer was removed, otherwise false. public bool Remove(IVisual layerRoot) { - Contract.Requires(layerRoot != null); - - SceneLayer layer; + _ = layerRoot ?? throw new ArgumentNullException(nameof(layerRoot)); - if (_index.TryGetValue(layerRoot, out layer)) + if (_index.TryGetValue(layerRoot, out var layer)) { Remove(layer); } @@ -179,7 +174,7 @@ namespace Avalonia.Rendering.SceneGraph /// True if the layer was part of the scene, otherwise false. public bool Remove(SceneLayer layer) { - Contract.Requires(layer != null); + _ = layer ?? throw new ArgumentNullException(nameof(layer)); _index.Remove(layer.LayerRoot); return _inner.Remove(layer); diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs index 4b6c331023..4a1587fb90 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs @@ -23,11 +23,11 @@ namespace Avalonia.Rendering.SceneGraph IBrush foreground, Point origin, IFormattedTextImpl text, - IDictionary childScenes = null) + IDictionary? childScenes = null) : base(text.Bounds.Translate(origin), transform) { Transform = transform; - Foreground = foreground?.ToImmutable(); + Foreground = foreground.ToImmutable(); Origin = origin; Text = text; ChildScenes = childScenes; @@ -54,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph public IFormattedTextImpl Text { get; } /// - public override IDictionary ChildScenes { get; } + public override IDictionary? ChildScenes { get; } /// public override void Render(IDrawingContextImpl context) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index db6b606b41..8a2f1f5073 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Disposables; -using Avalonia.Collections; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Utilities; @@ -19,9 +19,9 @@ namespace Avalonia.Rendering.SceneGraph private Rect? _bounds; private double _opacity; - private List _children; - private List> _drawOperations; - private IRef _drawOperationsRefCounter; + private List? _children; + private List>? _drawOperations; + private IRef? _drawOperationsRefCounter; private bool _drawOperationsCloned; private Matrix transformRestore; @@ -30,11 +30,9 @@ namespace Avalonia.Rendering.SceneGraph /// /// The visual that this node represents. /// The parent scene graph node, if any. - public VisualNode(IVisual visual, IVisualNode parent) + public VisualNode(IVisual visual, IVisualNode? parent) { - Contract.Requires(visual != null); - - Visual = visual; + Visual = visual ?? throw new ArgumentNullException(nameof(visual)); Parent = parent; HasAncestorGeometryClip = parent != null && (parent.HasAncestorGeometryClip || parent.GeometryClip != null); @@ -44,7 +42,7 @@ namespace Avalonia.Rendering.SceneGraph public IVisual Visual { get; } /// - public IVisualNode Parent { get; } + public IVisualNode? Parent { get; } /// public CornerRadius ClipToBoundsRadius { get; set; } @@ -65,7 +63,7 @@ namespace Avalonia.Rendering.SceneGraph public bool ClipToBounds { get; set; } /// - public IGeometryImpl GeometryClip { get; set; } + public IGeometryImpl? GeometryClip { get; set; } /// public bool HasAncestorGeometryClip { get; } @@ -87,7 +85,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets or sets the opacity mask for the scene graph node. /// - public IBrush OpacityMask { get; set; } + public IBrush? OpacityMask { get; set; } /// /// Gets a value indicating whether this node in the scene graph has already @@ -100,7 +98,7 @@ namespace Avalonia.Rendering.SceneGraph /// public bool OpacityChanged { get; private set; } - public IVisual LayerRoot { get; set; } + public IVisual? LayerRoot { get; set; } /// public IReadOnlyList Children => _children ?? EmptyChildren; @@ -259,7 +257,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// The new parent node. /// A cloned node. - public VisualNode Clone(IVisualNode parent) + public VisualNode Clone(IVisualNode? parent) { return new VisualNode(Visual, parent) { @@ -382,6 +380,7 @@ namespace Avalonia.Rendering.SceneGraph return result; } + [MemberNotNull(nameof(_children))] private void EnsureChildrenCreated(int capacity = 0) { if (_children == null) @@ -393,6 +392,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Ensures that this node draw operations have been created and are mutable (in case we are using cloned operations). /// + [MemberNotNull(nameof(_drawOperations))] private void EnsureDrawOperationsCreated() { if (_drawOperations == null) @@ -412,7 +412,7 @@ namespace Avalonia.Rendering.SceneGraph _drawOperations.Add(drawOperation.Clone()); } - _drawOperationsRefCounter.Dispose(); + _drawOperationsRefCounter?.Dispose(); _drawOperationsRefCounter = RefCountable.Create(CreateDisposeDrawOperations(_drawOperations)); _drawOperationsCloned = false; } diff --git a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs index 9cc94ffac3..86595754e9 100644 --- a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs +++ b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs @@ -6,7 +6,7 @@ namespace Avalonia.Rendering { public class SleepLoopRenderTimer : IRenderTimer { - private Action _tick; + private Action? _tick; private int _count; private readonly object _lock = new object(); private bool _running; diff --git a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs b/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs index e9700c70a5..4566b905c4 100644 --- a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs +++ b/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs @@ -9,6 +9,6 @@ namespace Avalonia.Rendering public static readonly ZIndexComparer Instance = new ZIndexComparer(); public static readonly Comparison ComparisonInstance = Instance.Compare; - public int Compare(IVisual x, IVisual y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0); + public int Compare(IVisual? x, IVisual? y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0); } } diff --git a/src/Avalonia.Visuals/RoundedRect.cs b/src/Avalonia.Visuals/RoundedRect.cs index 3452bc1ff8..7d17d681ef 100644 --- a/src/Avalonia.Visuals/RoundedRect.cs +++ b/src/Avalonia.Visuals/RoundedRect.cs @@ -9,7 +9,7 @@ namespace Avalonia return Rect.Equals(other.Rect) && RadiiTopLeft.Equals(other.RadiiTopLeft) && RadiiTopRight.Equals(other.RadiiTopRight) && RadiiBottomLeft.Equals(other.RadiiBottomLeft) && RadiiBottomRight.Equals(other.RadiiBottomRight); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is RoundedRect other && Equals(other); } diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index 8a805dc6c5..69c3ae7319 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -226,7 +226,7 @@ namespace Avalonia /// /// True if is a size that equals the current size. /// - public override bool Equals(object obj) => obj is Size other && Equals(other); + public override bool Equals(object? obj) => obj is Size other && Equals(other); /// /// Returns a hash code for a . diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs index 06ebc9bfe7..da3a98088f 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -252,7 +252,7 @@ namespace Avalonia /// /// True if is a size that equals the current size. /// - public override bool Equals(object obj) => obj is Thickness other && Equals(other); + public override bool Equals(object? obj) => obj is Thickness other && Equals(other); /// /// Returns a hash code for a . diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 322b630e83..78c6d9c057 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -572,7 +572,7 @@ namespace Avalonia /// /// The sender. /// The event args. - private void RenderTransformChanged(object sender, EventArgs e) + private void RenderTransformChanged(object? sender, EventArgs e) { InvalidateVisual(); } @@ -593,13 +593,14 @@ namespace Avalonia if (_visualRoot != null) { - var e = new VisualTreeAttachmentEventArgs(old, VisualRoot); + var e = new VisualTreeAttachmentEventArgs(old!, _visualRoot); OnDetachedFromVisualTreeCore(e); } if (_visualParent is IRenderRoot || _visualParent?.IsAttachedToVisualTree == true) { - var root = this.FindAncestorOfType(); + var root = this.FindAncestorOfType() ?? + throw new AvaloniaInternalException("Visual is atached to visual tree but root could not be found."); var e = new VisualTreeAttachmentEventArgs(_visualParent, root); OnAttachedToVisualTreeCore(e); } @@ -607,28 +608,28 @@ namespace Avalonia OnVisualParentChanged(old, value); } - private void AffectsRenderInvalidated(object sender, EventArgs e) => InvalidateVisual(); + private void AffectsRenderInvalidated(object? sender, EventArgs e) => InvalidateVisual(); /// /// Called when the collection changes. /// /// The sender. /// The event args. - private void VisualChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + private void VisualChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: - SetVisualParent(e.NewItems, this); + SetVisualParent(e.NewItems!, this); break; case NotifyCollectionChangedAction.Remove: - SetVisualParent(e.OldItems, null); + SetVisualParent(e.OldItems!, null); break; case NotifyCollectionChangedAction.Replace: - SetVisualParent(e.OldItems, null); - SetVisualParent(e.NewItems, this); + SetVisualParent(e.OldItems!, null); + SetVisualParent(e.NewItems!, this); break; } } @@ -639,7 +640,7 @@ namespace Avalonia for (var i = 0; i < count; i++) { - var visual = (Visual) children[i]; + var visual = (Visual) children[i]!; visual.SetVisualParent(parent); } diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index e6523a1469..ff8a515db3 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -16,8 +16,10 @@ namespace Avalonia /// The point in client coordinates. public static Point PointToClient(this IVisual visual, PixelPoint point) { - var rootPoint = visual.VisualRoot.PointToClient(point); - return visual.VisualRoot.TranslatePoint(rootPoint, visual).Value; + var root = visual.VisualRoot ?? + throw new ArgumentException("Control does not belong to a visual tree.", nameof(visual)); + var rootPoint = root.PointToClient(point); + return root.TranslatePoint(rootPoint, visual)!.Value; } /// @@ -28,8 +30,10 @@ namespace Avalonia /// The point in screen coordinates. public static PixelPoint PointToScreen(this IVisual visual, Point point) { - var p = visual.TranslatePoint(point, visual.VisualRoot); - return visual.VisualRoot.PointToScreen(p.Value); + var root = visual.VisualRoot ?? + throw new ArgumentException("Control does not belong to a visual tree.", nameof(visual)); + var p = visual.TranslatePoint(point, root); + return visual.VisualRoot.PointToScreen(p!.Value); } /// @@ -93,28 +97,29 @@ namespace Avalonia private static Matrix GetOffsetFrom(IVisual ancestor, IVisual visual) { var result = Matrix.Identity; + IVisual? v = visual; - while (visual != ancestor) + while (v != ancestor) { - if (visual.RenderTransform?.Value != null) + if (v.RenderTransform?.Value != null) { - var origin = visual.RenderTransformOrigin.ToPixels(visual.Bounds.Size); + var origin = v.RenderTransformOrigin.ToPixels(v.Bounds.Size); var offset = Matrix.CreateTranslation(origin); - var renderTransform = (-offset) * visual.RenderTransform.Value * (offset); + var renderTransform = (-offset) * v.RenderTransform.Value * (offset); result *= renderTransform; } - var topLeft = visual.Bounds.TopLeft; + var topLeft = v.Bounds.TopLeft; if (topLeft != default) { result *= Matrix.CreateTranslation(topLeft); } - visual = visual.VisualParent; + v = v.VisualParent; - if (visual == null) + if (v == null) { throw new ArgumentException("'visual' is not a descendant of 'ancestor'."); } diff --git a/src/Avalonia.Visuals/VisualTree/TransformedBounds.cs b/src/Avalonia.Visuals/VisualTree/TransformedBounds.cs index 3aa0392496..42f93d8edb 100644 --- a/src/Avalonia.Visuals/VisualTree/TransformedBounds.cs +++ b/src/Avalonia.Visuals/VisualTree/TransformedBounds.cs @@ -54,7 +54,7 @@ namespace Avalonia.VisualTree return Bounds == other.Bounds && Clip == other.Clip && Transform == other.Transform; } - public override bool Equals(object obj) => obj is TransformedBounds other && Equals(other); + public override bool Equals(object? obj) => obj is TransformedBounds other && Equals(other); public override int GetHashCode() { diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs index 8c4004efdc..128a39c082 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs @@ -19,20 +19,19 @@ namespace Avalonia.VisualTree /// The number of steps from the visual to the ancestor or -1 if /// is not a descendent of . /// - public static int CalculateDistanceFromAncestor(this IVisual visual, IVisual ancestor) + public static int CalculateDistanceFromAncestor(this IVisual visual, IVisual? ancestor) { - Contract.Requires(visual != null); - + IVisual? v = visual ?? throw new ArgumentNullException(nameof(visual)); var result = 0; - while (visual != null && visual != ancestor) + while (v != null && v != ancestor) { - visual = visual.VisualParent; + v = v.VisualParent; result++; } - return visual != null ? result : -1; + return v != null ? result : -1; } /// @@ -44,15 +43,14 @@ namespace Avalonia.VisualTree /// public static int CalculateDistanceFromRoot(IVisual visual) { - Contract.Requires(visual != null); - + IVisual? v = visual ?? throw new ArgumentNullException(nameof(visual)); var result = 0; - visual = visual?.VisualParent; + v = v?.VisualParent; - while (visual != null) + while (v != null) { - visual = visual.VisualParent; + v = v.VisualParent; result++; } @@ -66,54 +64,56 @@ namespace Avalonia.VisualTree /// The first visual. /// The second visual. /// The common ancestor, or null if not found. - public static IVisual FindCommonVisualAncestor(this IVisual visual, IVisual target) + public static IVisual? FindCommonVisualAncestor(this IVisual visual, IVisual target) { - Contract.Requires(visual != null); + IVisual? v = visual ?? throw new ArgumentNullException(nameof(visual)); if (target is null) { return null; } - void GoUpwards(ref IVisual node, int count) + IVisual? t = target; + + void GoUpwards(ref IVisual? node, int count) { for (int i = 0; i < count; ++i) { - node = node.VisualParent; + node = node?.VisualParent; } } // We want to find lowest node first, then make sure that both nodes are at the same height. // By doing that we can sometimes find out that other node is our lowest common ancestor. - var firstHeight = CalculateDistanceFromRoot(visual); - var secondHeight = CalculateDistanceFromRoot(target); + var firstHeight = CalculateDistanceFromRoot(v); + var secondHeight = CalculateDistanceFromRoot(t); if (firstHeight > secondHeight) { - GoUpwards(ref visual, firstHeight - secondHeight); + GoUpwards(ref v, firstHeight - secondHeight); } else { - GoUpwards(ref target, secondHeight - firstHeight); + GoUpwards(ref t, secondHeight - firstHeight); } - if (visual == target) + if (v == t) { - return visual; + return v; } - while (visual != null && target != null) + while (v != null && t != null) { - IVisual firstParent = visual.VisualParent; - IVisual secondParent = target.VisualParent; + IVisual? firstParent = v.VisualParent; + IVisual? secondParent = t.VisualParent; if (firstParent == secondParent) { return firstParent; } - visual = visual.VisualParent; - target = target.VisualParent; + v = v.VisualParent; + t = t.VisualParent; } return null; @@ -126,14 +126,14 @@ namespace Avalonia.VisualTree /// The visual's ancestors. public static IEnumerable GetVisualAncestors(this IVisual visual) { - Contract.Requires(visual != null); + IVisual? v = visual ?? throw new ArgumentNullException(nameof(visual)); - visual = visual.VisualParent; + v = v.VisualParent; - while (visual != null) + while (v != null) { - yield return visual; - visual = visual.VisualParent; + yield return v; + v = v.VisualParent; } } @@ -144,14 +144,14 @@ namespace Avalonia.VisualTree /// The visual. /// If given visual should be included in search. /// First ancestor of given type. - public static T FindAncestorOfType(this IVisual visual, bool includeSelf = false) where T : class + public static T? FindAncestorOfType(this IVisual visual, bool includeSelf = false) where T : class { if (visual is null) { return null; } - IVisual parent = includeSelf ? visual : visual.VisualParent; + IVisual? parent = includeSelf ? visual : visual.VisualParent; while (parent != null) { @@ -173,7 +173,7 @@ namespace Avalonia.VisualTree /// The visual. /// If given visual should be included in search. /// First descendant of given type. - public static T FindDescendantOfType(this IVisual visual, bool includeSelf = false) where T : class + public static T? FindDescendantOfType(this IVisual visual, bool includeSelf = false) where T : class { if (visual is null) { @@ -195,7 +195,7 @@ namespace Avalonia.VisualTree /// The visual and its ancestors. public static IEnumerable GetSelfAndVisualAncestors(this IVisual visual) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); yield return visual; @@ -211,9 +211,9 @@ namespace Avalonia.VisualTree /// The root visual to test. /// The point. /// The visual at the requested point. - public static IVisual GetVisualAt(this IVisual visual, Point p) + public static IVisual? GetVisualAt(this IVisual visual, Point p) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); return visual.GetVisualAt(p, x => x.IsVisible); } @@ -228,11 +228,17 @@ namespace Avalonia.VisualTree /// children will be excluded from the results. /// /// The visual at the requested point. - public static IVisual GetVisualAt(this IVisual visual, Point p, Func filter) + public static IVisual? GetVisualAt(this IVisual visual, Point p, Func filter) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); var root = visual.GetVisualRoot(); + + if (root is null) + { + return null; + } + var rootPoint = visual.TranslatePoint(p, root); if (rootPoint.HasValue) @@ -253,7 +259,7 @@ namespace Avalonia.VisualTree this IVisual visual, Point p) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); return visual.GetVisualsAt(p, x => x.IsVisible); } @@ -273,9 +279,15 @@ namespace Avalonia.VisualTree Point p, Func filter) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); var root = visual.GetVisualRoot(); + + if (root is null) + { + return Array.Empty(); + } + var rootPoint = visual.TranslatePoint(p, root); if (rootPoint.HasValue) @@ -334,7 +346,7 @@ namespace Avalonia.VisualTree /// /// The visual. /// The parent, or null if the visual is unparented. - public static IVisual GetVisualParent(this IVisual visual) + public static IVisual? GetVisualParent(this IVisual visual) { return visual.VisualParent; } @@ -347,7 +359,7 @@ namespace Avalonia.VisualTree /// /// The parent, or null if the visual is unparented or its parent is not of type . /// - public static T GetVisualParent(this IVisual visual) where T : class + public static T? GetVisualParent(this IVisual visual) where T : class { return visual.VisualParent as T; } @@ -359,9 +371,9 @@ namespace Avalonia.VisualTree /// /// The root visual or null if the visual is not rooted. /// - public static IRenderRoot GetVisualRoot(this IVisual visual) + public static IRenderRoot? GetVisualRoot(this IVisual visual) { - Contract.Requires(visual != null); + _ = visual ?? throw new ArgumentNullException(nameof(visual)); return visual as IRenderRoot ?? visual.VisualRoot; } @@ -377,7 +389,7 @@ namespace Avalonia.VisualTree /// public static bool IsVisualAncestorOf(this IVisual visual, IVisual target) { - IVisual current = target?.VisualParent; + IVisual? current = target?.VisualParent; while (current != null) { @@ -402,10 +414,10 @@ namespace Avalonia.VisualTree ZIndex = element.ZIndex, }) .OrderBy(x => x, null) - .Select(x => x.Element); + .Select(x => x.Element!); } - private static T FindDescendantOfTypeCore(IVisual visual) where T : class + private static T? FindDescendantOfTypeCore(IVisual visual) where T : class { var visualChildren = visual.VisualChildren; var visualChildrenCount = visualChildren.Count; @@ -432,12 +444,15 @@ namespace Avalonia.VisualTree private class ZOrderElement : IComparable { - public IVisual Element { get; set; } + public IVisual? Element { get; set; } public int Index { get; set; } public int ZIndex { get; set; } - public int CompareTo(ZOrderElement other) + public int CompareTo(ZOrderElement? other) { + if (other is null) + return 1; + var z = other.ZIndex - ZIndex; if (z != 0) diff --git a/src/Avalonia.Visuals/VisualTree/VisualLocator.cs b/src/Avalonia.Visuals/VisualTree/VisualLocator.cs index 1e0662c17a..940c87bafa 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualLocator.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualLocator.cs @@ -6,18 +6,18 @@ namespace Avalonia.VisualTree { public class VisualLocator { - public static IObservable Track(IVisual relativeTo, int ancestorLevel, Type ancestorType = null) + public static IObservable Track(IVisual relativeTo, int ancestorLevel, Type? ancestorType = null) { return new VisualTracker(relativeTo, ancestorLevel, ancestorType); } - private class VisualTracker : LightweightObservableBase + private class VisualTracker : LightweightObservableBase { private readonly IVisual _relativeTo; private readonly int _ancestorLevel; - private readonly Type _ancestorType; + private readonly Type? _ancestorType; - public VisualTracker(IVisual relativeTo, int ancestorLevel, Type ancestorType) + public VisualTracker(IVisual relativeTo, int ancestorLevel, Type? ancestorType) { _relativeTo = relativeTo; _ancestorLevel = ancestorLevel; @@ -36,14 +36,14 @@ namespace Avalonia.VisualTree _relativeTo.DetachedFromVisualTree -= AttachedDetached; } - protected override void Subscribed(IObserver observer, bool first) + protected override void Subscribed(IObserver observer, bool first) { observer.OnNext(GetResult()); } - private void AttachedDetached(object sender, VisualTreeAttachmentEventArgs e) => PublishNext(GetResult()); + private void AttachedDetached(object? sender, VisualTreeAttachmentEventArgs e) => PublishNext(GetResult()); - private IVisual GetResult() + private IVisual? GetResult() { if (_relativeTo.IsAttachedToVisualTree) { diff --git a/src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs b/src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs index d8f6ea8296..57a9340be8 100644 --- a/src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs +++ b/src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs @@ -17,11 +17,8 @@ namespace Avalonia /// The root visual. public VisualTreeAttachmentEventArgs(IVisual parent, IRenderRoot root) { - Contract.Requires(parent != null); - Contract.Requires(root != null); - - Parent = parent; - Root = root; + Parent = parent ?? throw new ArgumentNullException(nameof(parent)); + Root = root ?? throw new ArgumentNullException(nameof(root)); } /// diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs index 8cd4d7b111..a971c8e997 100644 --- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs +++ b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs @@ -106,8 +106,8 @@ namespace Avalonia.Web.Blazor public IRenderer CreateRenderer(IRenderRoot root) { - var loop = AvaloniaLocator.Current.GetService(); - + var loop = AvaloniaLocator.Current.GetService() ?? + throw new InvalidOperationException("Unable to locate IRenderLoop."); return new DeferredRenderer(root, loop); } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/TextNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/TextNodeTests.cs index 7c5ec5ddfd..a6eaea334c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/TextNodeTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/TextNodeTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Moq; @@ -15,7 +16,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph { var target = new TextNode( Matrix.Identity, - null, + Brushes.Black, new Point(10, 10), Mock.Of(x => x.Bounds == new Rect(5, 5, 50, 50)));