diff --git a/build/ImageSharp.props b/build/ImageSharp.props index 178c274ac9..66e6580070 100644 --- a/build/ImageSharp.props +++ b/build/ImageSharp.props @@ -1,5 +1,5 @@ - + diff --git a/build/Moq.props b/build/Moq.props index 9e2fd1db5d..357f0c9a5f 100644 --- a/build/Moq.props +++ b/build/Moq.props @@ -1,5 +1,5 @@  - + diff --git a/build/XUnit.props b/build/XUnit.props index 17ead91aa3..3c89c8b52b 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -1,13 +1,12 @@  - - - - - - - - + + + + + + + diff --git a/src/Avalonia.Base/Animation/KeySpline.cs b/src/Avalonia.Base/Animation/KeySpline.cs index 6ca5b2e759..ed6adb79b8 100644 --- a/src/Avalonia.Base/Animation/KeySpline.cs +++ b/src/Avalonia.Base/Animation/KeySpline.cs @@ -79,15 +79,12 @@ namespace Avalonia.Animation /// culture of the string /// Thrown if the string does not have 4 values /// A with the appropriate values set - public static KeySpline Parse(string value, CultureInfo culture) + public static KeySpline Parse(string value, CultureInfo? culture) { - if (culture is null) - culture = CultureInfo.InvariantCulture; + culture ??= CultureInfo.InvariantCulture; - using (var tokenizer = new StringTokenizer((string)value, culture, exceptionMessage: $"Invalid KeySpline string: \"{value}\".")) - { - return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble()); - } + using var tokenizer = new StringTokenizer(value, culture, exceptionMessage: $"Invalid KeySpline string: \"{value}\"."); + return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble()); } /// diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 1946d4ba5c..50a7a5c831 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -152,7 +152,7 @@ namespace Avalonia property = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); - _values?.ClearLocalValue(property); + _values.ClearLocalValue(property); } /// @@ -242,7 +242,14 @@ namespace Avalonia return registered.InvokeGetter(this); } - /// + /// + /// Gets an base value. + /// + /// The property. + /// + /// Gets the value of the property excluding animated values, otherwise . + /// Note that this method does not return property values that come from inherited or default values. + /// public Optional GetBaseValue(StyledProperty property) { _ = property ?? throw new ArgumentNullException(nameof(property)); @@ -261,7 +268,7 @@ namespace Avalonia VerifyAccess(); - return _values?.IsAnimating(property) ?? false; + return _values.IsAnimating(property); } /// @@ -279,7 +286,7 @@ namespace Avalonia VerifyAccess(); - return _values?.IsSet(property) ?? false; + return _values.IsSet(property); } /// @@ -515,14 +522,12 @@ namespace Avalonia /// The property. public void CoerceValue(AvaloniaProperty property) => _values.CoerceValue(property); - /// internal void AddInheritanceChild(AvaloniaObject child) { _inheritanceChildren ??= new List(); _inheritanceChildren.Add(child); } - - /// + internal void RemoveInheritanceChild(AvaloniaObject child) { _inheritanceChildren?.Remove(child); @@ -541,24 +546,11 @@ namespace Avalonia return new AvaloniaPropertyValue( property, GetValue(property), - BindingPriority.Unset, - "Local Value"); - } - else if (_values != null) - { - var result = _values.GetDiagnostic(property); - - if (result != null) - { - return result; - } + BindingPriority.LocalValue, + null); } - return new AvaloniaPropertyValue( - property, - GetValue(property), - BindingPriority.Unset, - "Unset"); + return _values.GetDiagnostic(property); } internal ValueStore GetValueStore() => _values; diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index f5c135459d..aeb71d16ae 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -30,7 +30,7 @@ namespace Avalonia.Data.Converters { if (value == null) { - return targetType.IsValueType ? AvaloniaProperty.UnsetValue : null; + return null; } if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1) diff --git a/src/Avalonia.Base/Diagnostics/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/Diagnostics/AvaloniaObjectExtensions.cs index d7b1f2e053..270cac95f2 100644 --- a/src/Avalonia.Base/Diagnostics/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/Diagnostics/AvaloniaObjectExtensions.cs @@ -1,6 +1,3 @@ -using System; -using Avalonia.Data; - namespace Avalonia.Diagnostics { /// diff --git a/src/Avalonia.Base/Input/DragEventArgs.cs b/src/Avalonia.Base/Input/DragEventArgs.cs index 403dd6f23e..8d7cc2b9a1 100644 --- a/src/Avalonia.Base/Input/DragEventArgs.cs +++ b/src/Avalonia.Base/Input/DragEventArgs.cs @@ -1,36 +1,28 @@ using System; using Avalonia.Interactivity; using Avalonia.Metadata; -using Avalonia.VisualTree; namespace Avalonia.Input { public class DragEventArgs : RoutedEventArgs { - private Interactive _target; - private Point _targetLocation; + private readonly Interactive _target; + private readonly Point _targetLocation; public DragDropEffects DragEffects { get; set; } - public IDataObject Data { get; private set; } + public IDataObject Data { get; } - public KeyModifiers KeyModifiers { get; private set; } + public KeyModifiers KeyModifiers { get; } public Point GetPosition(Visual relativeTo) { - var point = new Point(0, 0); - if (relativeTo == null) { throw new ArgumentNullException(nameof(relativeTo)); } - if (_target != null) - { - point = _target.TranslatePoint(_targetLocation, relativeTo) ?? point; - } - - return point; + return _target.TranslatePoint(_targetLocation, relativeTo) ?? new Point(0, 0); } [Unstable] diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index c6618fd550..9ee8ae9711 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -136,7 +136,7 @@ namespace Avalonia.Input return StringBuilderCache.GetStringAndRelease(s); } - public bool Matches(KeyEventArgs keyEvent) => + public bool Matches(KeyEventArgs? keyEvent) => keyEvent != null && keyEvent.KeyModifiers == KeyModifiers && ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key); diff --git a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs index b05d8f30bb..ba909de60f 100644 --- a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs +++ b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Avalonia.Input.Navigation; using Avalonia.VisualTree; @@ -51,7 +50,7 @@ namespace Avalonia.Input // If there's a custom keyboard navigation handler as an ancestor, use that. var custom = (element as Visual)?.FindAncestorOfType(true); - if (custom is object && HandlePreCustomNavigation(custom, element, direction, out var ce)) + if (custom is not null && HandlePreCustomNavigation(custom, element, direction, out var ce)) return ce; var result = direction switch @@ -117,32 +116,27 @@ namespace Avalonia.Input NavigationDirection direction, [NotNullWhen(true)] out IInputElement? result) { - if (customHandler != null) + var (handled, next) = customHandler.GetNext(element, direction); + + if (handled) { - var (handled, next) = customHandler.GetNext(element, direction); + if (next is not null) + { + result = next; + return true; + } - if (handled) + var r = direction switch { - if (next != null) - { - result = next; - return true; - } - else if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) - { - var r = direction switch - { - NavigationDirection.Next => TabNavigation.GetNextTabOutside(customHandler), - NavigationDirection.Previous => TabNavigation.GetPrevTabOutside(customHandler), - _ => throw new NotSupportedException(), - }; - - if (r is object) - { - result = r; - return true; - } - } + NavigationDirection.Next => TabNavigation.GetNextTabOutside(customHandler), + NavigationDirection.Previous => TabNavigation.GetPrevTabOutside(customHandler), + _ => null + }; + + if (r is not null) + { + result = r; + return true; } } diff --git a/src/Avalonia.Base/Input/Navigation/TabNavigation.cs b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs index d218867cf2..c460ecf3b3 100644 --- a/src/Avalonia.Base/Input/Navigation/TabNavigation.cs +++ b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using Avalonia.VisualTree; namespace Avalonia.Input.Navigation @@ -54,8 +52,7 @@ namespace Avalonia.Input.Navigation // Avoid the endless loop here for Cycle groups if (loopStartElement == nextTabElement) break; - if (loopStartElement == null) - loopStartElement = nextTabElement; + loopStartElement ??= nextTabElement; var firstTabElementInside = GetNextTab(null, nextTabElement, true); if (firstTabElementInside != null) @@ -80,12 +77,9 @@ namespace Avalonia.Input.Navigation public static IInputElement? GetNextTabOutside(ICustomKeyboardNavigation e) { - if (e is IInputElement container) + if (e is IInputElement container && GetLastInTree(container) is { } last) { - var last = GetLastInTree(container); - - if (last is object) - return GetNextTab(last, false); + return GetNextTab(last, false); } return null; @@ -93,11 +87,8 @@ namespace Avalonia.Input.Navigation public static IInputElement? GetPrevTab(IInputElement? e, IInputElement? container, bool goDownOnly) { - if (e is null && container is null) - throw new InvalidOperationException("Either 'e' or 'container' must be non-null."); - - if (container is null) - container = GetGroupParent(e!); + container ??= + GetGroupParent(e ?? throw new InvalidOperationException("Either 'e' or 'container' must be non-null.")); KeyboardNavigationMode tabbingType = GetKeyNavigationMode(container); @@ -163,8 +154,7 @@ namespace Avalonia.Input.Navigation // Avoid the endless loop here if (loopStartElement == nextTabElement) break; - if (loopStartElement == null) - loopStartElement = nextTabElement; + loopStartElement ??= nextTabElement; // At this point nextTabElement is TabGroup var lastTabElementInside = GetPrevTab(null, nextTabElement, true); @@ -189,22 +179,18 @@ namespace Avalonia.Input.Navigation public static IInputElement? GetPrevTabOutside(ICustomKeyboardNavigation e) { - if (e is IInputElement container) + if (e is IInputElement container && GetFirstChild(container) is { } first) { - var first = GetFirstChild(container); - - if (first is object) - return GetPrevTab(first, null, false); + return GetPrevTab(first, null, false); } return null; } - private static IInputElement? FocusedElement(IInputElement e) + private static IInputElement? FocusedElement(IInputElement? e) { - var iie = e; // Focus delegation is enabled only if keyboard focus is outside the container - if (iie != null && !iie.IsKeyboardFocusWithin) + if (e != null && !e.IsKeyboardFocusWithin) { var focusedElement = (FocusManager.Instance as FocusManager)?.GetFocusedElement(e); if (focusedElement != null) @@ -229,13 +215,11 @@ namespace Avalonia.Input.Navigation private static IInputElement? GetFirstChild(IInputElement e) { // If the element has a FocusedElement it should be its first child - if (FocusedElement(e) is IInputElement focusedElement) + if (FocusedElement(e) is { } focusedElement) return focusedElement; // Return the first visible element. - var uiElement = e as InputElement; - - if (uiElement is null || IsVisibleAndEnabled(uiElement)) + if (e is not InputElement uiElement || IsVisibleAndEnabled(uiElement)) { if (e is Visual elementAsVisual) { @@ -265,7 +249,7 @@ namespace Avalonia.Input.Navigation private static IInputElement? GetLastChild(IInputElement e) { // If the element has a FocusedElement it should be its last child - if (FocusedElement(e) is IInputElement focusedElement) + if (FocusedElement(e) is { } focusedElement) return focusedElement; // Return the last visible element. @@ -273,9 +257,7 @@ namespace Avalonia.Input.Navigation if (uiElement == null || IsVisibleAndEnabled(uiElement)) { - var elementAsVisual = e as Visual; - - if (elementAsVisual != null) + if (e is Visual elementAsVisual) { var children = elementAsVisual.VisualChildren; var count = children.Count; @@ -322,7 +304,7 @@ namespace Avalonia.Input.Navigation return firstTabElement; } - private static IInputElement? GetLastInTree(IInputElement container) + private static IInputElement GetLastInTree(IInputElement container) { IInputElement? result; IInputElement? c = container; diff --git a/src/Avalonia.Base/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Base/LogicalTree/LogicalExtensions.cs index 74720c0a77..9ab7da3ff7 100644 --- a/src/Avalonia.Base/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Base/LogicalTree/LogicalExtensions.cs @@ -48,7 +48,7 @@ namespace Avalonia.LogicalTree /// The logical. /// If given logical should be included in search. /// First ancestor of given type. - public static T? FindLogicalAncestorOfType(this ILogical logical, bool includeSelf = false) where T : class + public static T? FindLogicalAncestorOfType(this ILogical? logical, bool includeSelf = false) where T : class { if (logical is null) { @@ -120,7 +120,7 @@ namespace Avalonia.LogicalTree /// The logical. /// If given logical should be included in search. /// First descendant of given type. - public static T? FindLogicalDescendantOfType(this ILogical logical, bool includeSelf = false) where T : class + public static T? FindLogicalDescendantOfType(this ILogical? logical, bool includeSelf = false) where T : class { if (logical is null) { @@ -185,7 +185,7 @@ namespace Avalonia.LogicalTree /// True if is an ancestor of ; /// otherwise false. /// - public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) + public static bool IsLogicalAncestorOf(this ILogical? logical, ILogical? target) { var current = target?.LogicalParent; diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 5470a735b3..ab89177295 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -147,16 +147,11 @@ namespace Avalonia.Media /// The color string. /// The parsed color /// The status of the operation. - public static bool TryParse(string s, out Color color) + public static bool TryParse(string? s, out Color color) { color = default; - if (s is null) - { - return false; - } - - if (s.Length == 0) + if (string.IsNullOrEmpty(s)) { return false; } diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs index d295111d72..622181dba0 100644 --- a/src/Avalonia.Base/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -240,7 +240,7 @@ namespace Avalonia.Media /// /// The foreground brush. /// The glyph run. - public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun) + public void DrawGlyphRun(IBrush? foreground, GlyphRun glyphRun) { _ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun)); diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs index 481329c20c..b7abda2c61 100644 --- a/src/Avalonia.Base/Media/DrawingGroup.cs +++ b/src/Avalonia.Base/Media/DrawingGroup.cs @@ -13,14 +13,14 @@ namespace Avalonia.Media public static readonly StyledProperty OpacityProperty = AvaloniaProperty.Register(nameof(Opacity), 1); - public static readonly StyledProperty TransformProperty = - AvaloniaProperty.Register(nameof(Transform)); + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); - public static readonly StyledProperty ClipGeometryProperty = - AvaloniaProperty.Register(nameof(ClipGeometry)); + public static readonly StyledProperty ClipGeometryProperty = + AvaloniaProperty.Register(nameof(ClipGeometry)); - public static readonly StyledProperty OpacityMaskProperty = - AvaloniaProperty.Register(nameof(OpacityMask)); + public static readonly StyledProperty OpacityMaskProperty = + AvaloniaProperty.Register(nameof(OpacityMask)); public static readonly DirectProperty ChildrenProperty = AvaloniaProperty.RegisterDirect( @@ -36,19 +36,19 @@ namespace Avalonia.Media set => SetValue(OpacityProperty, value); } - public Transform Transform + public Transform? Transform { get => GetValue(TransformProperty); set => SetValue(TransformProperty, value); } - public Geometry ClipGeometry + public Geometry? ClipGeometry { get => GetValue(ClipGeometryProperty); set => SetValue(ClipGeometryProperty, value); } - public IBrush OpacityMask + public IBrush? OpacityMask { get => GetValue(OpacityMaskProperty); set => SetValue(OpacityMaskProperty, value); @@ -159,7 +159,7 @@ namespace Avalonia.Media public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry) { - if (((brush == null) && (pen == null)) || (geometry == null)) + if ((brush == null) && (pen == null)) { return; } @@ -167,9 +167,9 @@ namespace Avalonia.Media AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); } - public void DrawGlyphRun(IBrush foreground, IRef glyphRun) + public void DrawGlyphRun(IBrush? foreground, IRef glyphRun) { - if (foreground == null || glyphRun == null) + if (foreground == null) { return; } @@ -184,7 +184,7 @@ namespace Avalonia.Media AddDrawing(glyphRunDrawing); } - public void DrawLine(IPen pen, Point p1, Point p2) + public void DrawLine(IPen? pen, Point p1, Point p2) { if (pen == null) { diff --git a/src/Avalonia.Base/Media/DrawingImage.cs b/src/Avalonia.Base/Media/DrawingImage.cs index 38ddbdfaed..1b22a1ee69 100644 --- a/src/Avalonia.Base/Media/DrawingImage.cs +++ b/src/Avalonia.Base/Media/DrawingImage.cs @@ -20,8 +20,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty DrawingProperty = - AvaloniaProperty.Register(nameof(Drawing)); + public static readonly StyledProperty DrawingProperty = + AvaloniaProperty.Register(nameof(Drawing)); /// public event EventHandler? Invalidated; @@ -30,7 +30,7 @@ namespace Avalonia.Media /// Gets or sets the drawing content. /// [Content] - public Drawing Drawing + public Drawing? Drawing { get => GetValue(DrawingProperty); set => SetValue(DrawingProperty, value); diff --git a/src/Avalonia.Base/Media/FontFamily.cs b/src/Avalonia.Base/Media/FontFamily.cs index da84861668..f4406bd010 100644 --- a/src/Avalonia.Base/Media/FontFamily.cs +++ b/src/Avalonia.Base/Media/FontFamily.cs @@ -119,7 +119,7 @@ namespace Avalonia.Media case 2: { - var source = segments[0].StartsWith("/") + var source = segments[0].StartsWith("/", StringComparison.Ordinal) ? new Uri(segments[0], UriKind.Relative) : new Uri(segments[0], UriKind.RelativeOrAbsolute); @@ -188,7 +188,7 @@ namespace Avalonia.Media { unchecked { - return ((FamilyNames != null ? FamilyNames.GetHashCode() : 0) * 397) ^ (Key != null ? Key.GetHashCode() : 0); + return (FamilyNames.GetHashCode() * 397) ^ (Key is not null ? Key.GetHashCode() : 0); } } diff --git a/src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs index f607c67fed..12bb7e77e7 100644 --- a/src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs +++ b/src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs @@ -41,10 +41,7 @@ namespace Avalonia.Media.Fonts { var hash = (int)2166136261; - if (Source != null) - { - hash = (hash * 16777619) ^ Source.GetHashCode(); - } + hash = (hash * 16777619) ^ Source.GetHashCode(); if (BaseUri != null) { diff --git a/src/Avalonia.Base/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs index 28757b1a1d..3b63a98720 100644 --- a/src/Avalonia.Base/Media/FormattedText.cs +++ b/src/Avalonia.Base/Media/FormattedText.cs @@ -1354,7 +1354,7 @@ namespace Avalonia.Media { var highlightBounds = currentLine.GetTextBounds(x0,x1 - x0); - if (highlightBounds != null) + if (highlightBounds.Count > 0) { foreach (var bound in highlightBounds) { @@ -1365,7 +1365,7 @@ namespace Avalonia.Media // Convert logical units (which extend leftward from the right edge // of the paragraph) to physical units. // - // Note that since rect is in logical units, rect.Right corresponds to + // Note that since rect is in logical units, rect.Right corresponds to // the visual *left* edge of the rectangle in the RTL case. Specifically, // is the distance leftward from the right edge of the formatting rectangle // whose width is the paragraph width passed to FormatLine. @@ -1384,7 +1384,7 @@ namespace Avalonia.Media else { accumulatedBounds = Geometry.Combine(accumulatedBounds, rectangleGeometry, GeometryCombineMode.Union); - } + } } } } diff --git a/src/Avalonia.Base/Media/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs index 26cc2c3cab..ac2dce1e42 100644 --- a/src/Avalonia.Base/Media/GeometryDrawing.cs +++ b/src/Avalonia.Base/Media/GeometryDrawing.cs @@ -15,8 +15,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty GeometryProperty = - AvaloniaProperty.Register(nameof(Geometry)); + public static readonly StyledProperty GeometryProperty = + AvaloniaProperty.Register(nameof(Geometry)); /// /// Defines the property. @@ -34,7 +34,7 @@ namespace Avalonia.Media /// Gets or sets the that describes the shape of this . /// [Content] - public Geometry Geometry + public Geometry? Geometry { get => GetValue(GeometryProperty); set => SetValue(GeometryProperty, value); diff --git a/src/Avalonia.Base/Media/GlyphRunDrawing.cs b/src/Avalonia.Base/Media/GlyphRunDrawing.cs index 242b9913fa..06d92fd81c 100644 --- a/src/Avalonia.Base/Media/GlyphRunDrawing.cs +++ b/src/Avalonia.Base/Media/GlyphRunDrawing.cs @@ -2,19 +2,19 @@ { public class GlyphRunDrawing : Drawing { - public static readonly StyledProperty ForegroundProperty = - AvaloniaProperty.Register(nameof(Foreground)); + public static readonly StyledProperty ForegroundProperty = + AvaloniaProperty.Register(nameof(Foreground)); - public static readonly StyledProperty GlyphRunProperty = - AvaloniaProperty.Register(nameof(GlyphRun)); + public static readonly StyledProperty GlyphRunProperty = + AvaloniaProperty.Register(nameof(GlyphRun)); - public IBrush Foreground + public IBrush? Foreground { get => GetValue(ForegroundProperty); set => SetValue(ForegroundProperty, value); } - public GlyphRun GlyphRun + public GlyphRun? GlyphRun { get => GetValue(GlyphRunProperty); set => SetValue(GlyphRunProperty, value); diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index 425a3138c3..b4bf6fd217 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -254,7 +254,7 @@ namespace Avalonia.Media /// The HSL color string to parse. /// The parsed . /// True if parsing was successful; otherwise, false. - public static bool TryParse(string s, out HslColor hslColor) + public static bool TryParse(string? s, out HslColor hslColor) { bool prefixMatched = false; diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 9f95b31518..f97457c54d 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -254,7 +254,7 @@ namespace Avalonia.Media /// The HSV color string to parse. /// The parsed . /// True if parsing was successful; otherwise, false. - public static bool TryParse(string s, out HsvColor hsvColor) + public static bool TryParse(string? s, out HsvColor hsvColor) { bool prefixMatched = false; diff --git a/src/Avalonia.Base/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs index 6662613ff4..a7d3e4da10 100644 --- a/src/Avalonia.Base/Media/IVisualBrush.cs +++ b/src/Avalonia.Base/Media/IVisualBrush.cs @@ -1,5 +1,4 @@ using Avalonia.Metadata; -using Avalonia.VisualTree; namespace Avalonia.Media { @@ -12,6 +11,6 @@ namespace Avalonia.Media /// /// Gets the visual to draw. /// - Visual Visual { get; } + Visual? Visual { get; } } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs index 1f53f06955..6dff006045 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs @@ -39,17 +39,8 @@ namespace Avalonia.Media.Immutable { return true; } - else if (other is null) - { - return false; - } - if (Offset != other.Offset) - { - return false; - } - - return SequenceEqual(Dashes, other.Dashes); + return other is not null && Offset == other.Offset && SequenceEqual(_dashes, other.Dashes); } /// @@ -58,30 +49,27 @@ namespace Avalonia.Media.Immutable var hashCode = 717868523; hashCode = hashCode * -1521134295 + Offset.GetHashCode(); - if (_dashes != null) + foreach (var i in _dashes) { - foreach (var i in _dashes) - { - hashCode = hashCode * -1521134295 + i.GetHashCode(); - } + hashCode = hashCode * -1521134295 + i.GetHashCode(); } return hashCode; } - private static bool SequenceEqual(IReadOnlyList left, IReadOnlyList? right) + private static bool SequenceEqual(double[] left, IReadOnlyList? right) { if (ReferenceEquals(left, right)) { return true; } - if (left == null || right == null || left.Count != right.Count) + if (right is null || left.Length != right.Count) { return false; } - for (var c = 0; c < left.Count; c++) + for (var c = 0; c < left.Length; c++) { if (left[c] != right[c]) { diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 9b443391c5..0b625080e3 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -1,5 +1,4 @@ using Avalonia.Media.Imaging; -using Avalonia.VisualTree; namespace Avalonia.Media.Immutable { @@ -31,11 +30,11 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, - RelativePoint transformOrigin = new RelativePoint(), + RelativePoint transformOrigin = default, RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, - Imaging.BitmapInterpolationMode bitmapInterpolationMode = Imaging.BitmapInterpolationMode.Default) + BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) : base( alignmentX, alignmentY, @@ -62,6 +61,6 @@ namespace Avalonia.Media.Immutable } /// - public Visual Visual { get; } + public Visual? Visual { get; } } } diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs index dc9e5cb907..b74b7df9c5 100644 --- a/src/Avalonia.Base/Media/TextDecoration.cs +++ b/src/Avalonia.Base/Media/TextDecoration.cs @@ -22,8 +22,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty StrokeProperty = - AvaloniaProperty.Register(nameof(Stroke)); + public static readonly StyledProperty StrokeProperty = + AvaloniaProperty.Register(nameof(Stroke)); /// /// Defines the property. @@ -34,8 +34,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty> StrokeDashArrayProperty = - AvaloniaProperty.Register>(nameof(StrokeDashArray)); + public static readonly StyledProperty?> StrokeDashArrayProperty = + AvaloniaProperty.Register?>(nameof(StrokeDashArray)); /// /// Defines the property. @@ -82,7 +82,7 @@ namespace Avalonia.Media /// /// Gets or sets the that specifies how the is painted. /// - public IBrush Stroke + public IBrush? Stroke { get { return GetValue(StrokeProperty); } set { SetValue(StrokeProperty, value); } @@ -101,7 +101,7 @@ namespace Avalonia.Media /// Gets or sets a collection of values that indicate the pattern of dashes and gaps /// that is used to draw the . /// - public AvaloniaList StrokeDashArray + public AvaloniaList? StrokeDashArray { get { return GetValue(StrokeDashArrayProperty); } set { SetValue(StrokeDashArrayProperty, value); } @@ -220,7 +220,7 @@ namespace Avalonia.Media var intersections = glyphRun.PlatformImpl.Item.GetIntersections((float)(thickness * 0.5d - offsetY), (float)(thickness * 1.5d - offsetY)); - if (intersections != null && intersections.Count > 0) + if (intersections.Count > 0) { var last = baselineOrigin.X; var finalPos = last + glyphRun.Size.Width; diff --git a/src/Avalonia.Base/Media/VisualBrush.cs b/src/Avalonia.Base/Media/VisualBrush.cs index 1261d233ac..2be3e9a94e 100644 --- a/src/Avalonia.Base/Media/VisualBrush.cs +++ b/src/Avalonia.Base/Media/VisualBrush.cs @@ -1,5 +1,4 @@ using Avalonia.Media.Immutable; -using Avalonia.VisualTree; namespace Avalonia.Media { @@ -11,8 +10,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty VisualProperty = - AvaloniaProperty.Register(nameof(Visual)); + public static readonly StyledProperty VisualProperty = + AvaloniaProperty.Register(nameof(Visual)); static VisualBrush() { @@ -38,7 +37,7 @@ namespace Avalonia.Media /// /// Gets or sets the visual to draw. /// - public Visual Visual + public Visual? Visual { get { return GetValue(VisualProperty); } set { SetValue(VisualProperty, value); } diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index c05c04c22e..8509067cd0 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -49,7 +49,7 @@ namespace Avalonia.Platform /// The stroke pen. /// The first point of the line. /// The second point of the line. - void DrawLine(IPen pen, Point p1, Point p2); + void DrawLine(IPen? pen, Point p1, Point p2); /// /// Draws a geometry. @@ -91,7 +91,7 @@ namespace Avalonia.Platform /// /// The foreground. /// The glyph run. - void DrawGlyphRun(IBrush foreground, IRef glyphRun); + void DrawGlyphRun(IBrush? foreground, IRef glyphRun); /// /// Creates a new that can be used as a render layer diff --git a/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs index 467cd530fc..d1a803fefb 100644 --- a/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs +++ b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs @@ -19,25 +19,20 @@ internal class AssemblyDescriptor : IAssemblyDescriptor public AssemblyDescriptor(Assembly assembly) { Assembly = assembly; + Resources = assembly.GetManifestResourceNames() + .ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n)); + Name = assembly.GetName().Name; - if (assembly != null) + using var resources = assembly.GetManifestResourceStream(Constants.AvaloniaResourceName); + if (resources != null) { - Resources = assembly.GetManifestResourceNames() - .ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n)); - Name = assembly.GetName().Name; - using (var resources = assembly.GetManifestResourceStream(Constants.AvaloniaResourceName)) - { - if (resources != null) - { - Resources.Remove(Constants.AvaloniaResourceName); + Resources.Remove(Constants.AvaloniaResourceName); - var indexLength = new BinaryReader(resources).ReadInt32(); - var index = AvaloniaResourcesIndexReaderWriter.ReadIndex(new SlicedStream(resources, 4, indexLength)); - var baseOffset = indexLength + 4; - AvaloniaResources = index.ToDictionary(r => GetPathRooted(r), r => (IAssetDescriptor) - new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size)); - } - } + var indexLength = new BinaryReader(resources).ReadInt32(); + var index = AvaloniaResourcesIndexReaderWriter.ReadIndex(new SlicedStream(resources, 4, indexLength)); + var baseOffset = indexLength + 4; + AvaloniaResources = index.ToDictionary(GetPathRooted, r => (IAssetDescriptor) + new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size)); } } @@ -45,6 +40,7 @@ internal class AssemblyDescriptor : IAssemblyDescriptor public Dictionary? Resources { get; } public Dictionary? AvaloniaResources { get; } public string? Name { get; } + private static string GetPathRooted(AvaloniaResourcesIndexEntry r) => r.Path![0] == '/' ? r.Path : '/' + r.Path; } diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs index a6db4330a3..455e9ebb5f 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs @@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition.Animations public abstract class CompositionAnimation : CompositionObject, ICompositionAnimationBase { private readonly CompositionPropertySet _propertySet; - internal CompositionAnimation(Compositor compositor) : base(compositor, null!) + internal CompositionAnimation(Compositor compositor) : base(compositor, null) { _propertySet = new CompositionPropertySet(compositor); } diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs index bad3991f43..1500e88abe 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs @@ -19,7 +19,7 @@ namespace Avalonia.Rendering.Composition.Animations public void Remove(CompositionAnimation value) => Animations.Remove(value); public void RemoveAll() => Animations.Clear(); - public CompositionAnimationGroup(Compositor compositor) : base(compositor, null!) + public CompositionAnimationGroup(Compositor compositor) : base(compositor, null) { } } diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs b/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs index 72be4edd07..d9adf261f8 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs @@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition.Animations { private Dictionary _inner = new Dictionary(); private IDictionary _innerface; - internal ImplicitAnimationCollection(Compositor compositor) : base(compositor, null!) + internal ImplicitAnimationCollection(Compositor compositor) : base(compositor, null) { _innerface = _inner; } diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 668d650ffd..7fa2d4955f 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -20,15 +20,17 @@ public class CompositingRenderer : IRendererWithCompositor { private readonly IRenderRoot _root; private readonly Compositor _compositor; - CompositionDrawingContext _recorder = new(); - DrawingContext _recordingContext; - private HashSet _dirty = new(); - private HashSet _recalculateChildren = new(); + private readonly CompositionDrawingContext _recorder = new(); + private readonly DrawingContext _recordingContext; + private readonly HashSet _dirty = new(); + private readonly HashSet _recalculateChildren = new(); + private readonly Action _update; + private bool _queuedUpdate; - private Action _update; private bool _updating; + private bool _isDisposed; - internal CompositionTarget CompositionTarget; + internal CompositionTarget CompositionTarget { get; } /// /// Asks the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered. @@ -38,6 +40,17 @@ public class CompositingRenderer : IRendererWithCompositor /// public RendererDiagnostics Diagnostics { get; } + /// + public Compositor Compositor => _compositor; + + /// + /// Initializes a new instance of + /// + /// The render root using this renderer. + /// The associated compositors. + /// + /// A function returning the list of native platform's surfaces that can be consumed by rendering subsystems. + /// public CompositingRenderer(IRenderRoot root, Compositor compositor, Func> surfaces) { _root = root; @@ -66,7 +79,7 @@ public class CompositingRenderer : IRendererWithCompositor /// public event EventHandler? SceneInvalidated; - void QueueUpdate() + private void QueueUpdate() { if(_queuedUpdate) return; @@ -77,9 +90,11 @@ public class CompositingRenderer : IRendererWithCompositor /// public void AddDirty(Visual visual) { + if (_isDisposed) + return; if (_updating) throw new InvalidOperationException("Visual was invalidated during the render pass"); - _dirty.Add((Visual)visual); + _dirty.Add(visual); QueueUpdate(); } @@ -126,9 +141,11 @@ public class CompositingRenderer : IRendererWithCompositor /// public void RecalculateChildren(Visual visual) { + if (_isDisposed) + return; if (_updating) throw new InvalidOperationException("Visual was invalidated during the render pass"); - _recalculateChildren.Add((Visual)visual); + _recalculateChildren.Add(visual); QueueUpdate(); } @@ -171,7 +188,7 @@ public class CompositingRenderer : IRendererWithCompositor if (sortedChildren != null) for (var c = 0; c < visualChildren.Count; c++) { - if (!ReferenceEquals(compositionChildren[c], ((Visual)sortedChildren[c].visual).CompositionVisual)) + if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual)) { mismatch = true; break; @@ -179,7 +196,7 @@ public class CompositingRenderer : IRendererWithCompositor } else for (var c = 0; c < visualChildren.Count; c++) - if (!ReferenceEquals(compositionChildren[c], ((Visual)visualChildren[c]).CompositionVisual)) + if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual)) { mismatch = true; break; @@ -201,7 +218,7 @@ public class CompositingRenderer : IRendererWithCompositor { foreach (var ch in sortedChildren) { - var compositionChild = ((Visual)ch.visual).CompositionVisual; + var compositionChild = ch.visual.CompositionVisual; if (compositionChild != null) compositionChildren.Add(compositionChild); } @@ -210,7 +227,7 @@ public class CompositingRenderer : IRendererWithCompositor else foreach (var ch in v.GetVisualChildren()) { - var compositionChild = ((Visual)ch).CompositionVisual; + var compositionChild = ch.CompositionVisual; if (compositionChild != null) compositionChildren.Add(compositionChild); } @@ -289,13 +306,18 @@ public class CompositingRenderer : IRendererWithCompositor _updating = false; } } - + + /// public void Resized(Size size) { } + /// public void Paint(Rect rect) { + if (_isDisposed) + return; + QueueUpdate(); CompositionTarget.RequestRedraw(); if(RenderOnlyOnRenderThread && Compositor.Loop.RunsInBackground) @@ -304,17 +326,34 @@ public class CompositingRenderer : IRendererWithCompositor CompositionTarget.ImmediateUIThreadRender(); } - public void Start() => CompositionTarget.IsEnabled = true; - - public void Stop() + /// + public void Start() { - CompositionTarget.IsEnabled = false; + if (_isDisposed) + return; + + CompositionTarget.IsEnabled = true; } - public ValueTask TryGetRenderInterfaceFeature(Type featureType) => Compositor.TryGetRenderInterfaceFeature(featureType); + /// + public void Stop() + => CompositionTarget.IsEnabled = false; + + /// + public ValueTask TryGetRenderInterfaceFeature(Type featureType) + => Compositor.TryGetRenderInterfaceFeature(featureType); + /// public void Dispose() { + if (_isDisposed) + return; + + _isDisposed = true; + _dirty.Clear(); + _recalculateChildren.Clear(); + SceneInvalidated = null; + Stop(); CompositionTarget.Dispose(); @@ -323,9 +362,4 @@ public class CompositingRenderer : IRendererWithCompositor if (Compositor.Loop.RunsInBackground) _compositor.Commit().Wait(); } - - /// - /// The associated object - /// - public Compositor Compositor => _compositor; } diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionDrawingSurface.cs b/src/Avalonia.Base/Rendering/Composition/CompositionDrawingSurface.cs index ab4329df62..bfe70d593d 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionDrawingSurface.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionDrawingSurface.cs @@ -1,4 +1,3 @@ -using System; using System.Threading.Tasks; using Avalonia.Rendering.Composition.Server; using Avalonia.Threading; @@ -7,7 +6,7 @@ namespace Avalonia.Rendering.Composition; public class CompositionDrawingSurface : CompositionSurface { - internal new ServerCompositionDrawingSurface Server => (ServerCompositionDrawingSurface)base.Server; + internal new ServerCompositionDrawingSurface Server => (ServerCompositionDrawingSurface)base.Server!; internal CompositionDrawingSurface(Compositor compositor) : base(compositor, new ServerCompositionDrawingSurface(compositor.Server)) { } diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs b/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs index 50332926ad..8c21b534db 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionObject.cs @@ -22,7 +22,7 @@ namespace Avalonia.Rendering.Composition public ImplicitAnimationCollection? ImplicitAnimations { get; set; } private protected InlineDictionary PendingAnimations; - internal CompositionObject(Compositor compositor, ServerObject server) + internal CompositionObject(Compositor compositor, ServerObject? server) { Compositor = compositor; Server = server; @@ -32,7 +32,7 @@ namespace Avalonia.Rendering.Composition /// The associated Compositor /// public Compositor Compositor { get; } - internal ServerObject Server { get; } + internal ServerObject? Server { get; } public bool IsDisposed { get; private set; } private bool _registeredForSerialization; diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs b/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs index 7d794af9a2..efd89951bb 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs @@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition private readonly Dictionary _variants = new Dictionary(); private readonly Dictionary _objects = new Dictionary(); - internal CompositionPropertySet(Compositor compositor) : base(compositor, null!) + internal CompositionPropertySet(Compositor compositor) : base(compositor, null) { } diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs index 05488a558f..b75d080cfd 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs @@ -88,8 +88,13 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW } /// - public void DrawLine(IPen pen, Point p1, Point p2) + public void DrawLine(IPen? pen, Point p1, Point p2) { + if (pen is null) + { + return; + } + var next = NextDrawAs(); if (next == null || !next.Item.Equals(Transform, pen, p1, p2)) @@ -159,8 +164,13 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW public object? GetFeature(Type t) => null; /// - public void DrawGlyphRun(IBrush foreground, IRef glyphRun) + public void DrawGlyphRun(IBrush? foreground, IRef glyphRun) { + if (foreground is null) + { + return; + } + var next = NextDrawAs(); if (next == null || !next.Item.Equals(Transform, foreground, glyphRun)) diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs index 560ee05c10..b15da5d05d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs @@ -165,8 +165,6 @@ namespace Avalonia.Rendering.Composition.Expressions public override ExpressionVariant Evaluate(ref ExpressionEvaluationContext context) { - if (context.ForeignFunctionInterface == null) - return default; var args = new List(); foreach (var expr in Parameters) args.Add(expr.Evaluate(ref context)); diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs index 9086c59aad..f268364b54 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Avalonia.Rendering.Composition.Server; // Special license applies License.md diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs index c58beebe7f..50df8bd32b 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs @@ -66,7 +66,7 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont _impl.DrawBitmap(source, opacityMask, opacityMaskRect, destRect); } - public void DrawLine(IPen pen, Point p1, Point p2) + public void DrawLine(IPen? pen, Point p1, Point p2) { _impl.DrawLine(pen, p1, p2); } @@ -86,7 +86,7 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont _impl.DrawEllipse(brush, pen, rect); } - public void DrawGlyphRun(IBrush foreground, IRef glyphRun) + public void DrawGlyphRun(IBrush? foreground, IRef glyphRun) { _impl.DrawGlyphRun(foreground, glyphRun); } diff --git a/src/Avalonia.Base/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs index fa3260ffb4..09df7b7830 100644 --- a/src/Avalonia.Base/Rendering/IRenderRoot.cs +++ b/src/Avalonia.Base/Rendering/IRenderRoot.cs @@ -1,6 +1,4 @@ using Avalonia.Metadata; -using Avalonia.Platform; -using Avalonia.VisualTree; namespace Avalonia.Rendering { diff --git a/src/Avalonia.Base/Rendering/IRenderer.cs b/src/Avalonia.Base/Rendering/IRenderer.cs index ba960ff5f3..7e32504e17 100644 --- a/src/Avalonia.Base/Rendering/IRenderer.cs +++ b/src/Avalonia.Base/Rendering/IRenderer.cs @@ -90,6 +90,9 @@ namespace Avalonia.Rendering public interface IRendererWithCompositor : IRenderer { + /// + /// The associated object + /// Compositor Compositor { get; } } } diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index c67ac7057d..8e5dc38317 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -48,8 +48,10 @@ namespace Avalonia.Rendering /// void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush) { - var visual = brush.Visual; - Render(new DrawingContext(context), visual, visual.Bounds); + if (brush.Visual is { } visual) + { + Render(new DrawingContext(context), visual, visual.Bounds); + } } internal static void Render(Visual visual, DrawingContext context, bool updateTransformedBounds) diff --git a/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs index 12b67105e9..82f8fc2d56 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs @@ -80,11 +80,8 @@ namespace Avalonia.Rendering.SceneGraph { p *= Transform.Invert(); - if (Material != null) - { - var rect = Rect.Rect; - return rect.ContainsExclusive(p); - } + var rect = Rect.Rect; + return rect.ContainsExclusive(p); } return false; diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs index 3c44dd63ce..fafafabd82 100644 --- a/src/Avalonia.Base/Utilities/TypeUtilities.cs +++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs @@ -212,7 +212,7 @@ namespace Avalonia.Utilities var toTypeConverter = TypeDescriptor.GetConverter(toUnderl); - if (toTypeConverter.CanConvertFrom(from) == true) + if (toTypeConverter.CanConvertFrom(from)) { result = toTypeConverter.ConvertFrom(null, culture, value); return true; @@ -220,7 +220,7 @@ namespace Avalonia.Utilities var fromTypeConverter = TypeDescriptor.GetConverter(from); - if (fromTypeConverter.CanConvertTo(toUnderl) == true) + if (fromTypeConverter.CanConvertTo(toUnderl)) { result = fromTypeConverter.ConvertTo(null, culture, value, toUnderl); return true; @@ -329,7 +329,7 @@ namespace Avalonia.Utilities } [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)] - public static T ConvertImplicit(object value) + public static T ConvertImplicit(object? value) { if (TryConvertImplicit(typeof(T), value, out var result)) { @@ -369,11 +369,6 @@ namespace Avalonia.Utilities /// public static bool IsNumeric(Type type) { - if (type == null) - { - return false; - } - var underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index e72606bf70..237a491615 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; using Avalonia.Threading; @@ -15,7 +11,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { private readonly Func, Action> _subscribe; - readonly ConditionalWeakTable _subscriptions = new(); + private readonly ConditionalWeakTable _subscriptions = new(); internal WeakEvent( Action> subscribe, @@ -51,56 +47,6 @@ public class WeakEvent : WeakEvent where TEventArgs : Event private readonly WeakEvent _ev; private readonly TSender _target; private readonly Action _compact; - - struct Entry - { - WeakReference>? _reference; - int _hashCode; - - public Entry(IWeakEventSubscriber r) - { - if (r == null) - { - _reference = null; - _hashCode = 0; - return; - } - - _hashCode = r.GetHashCode(); - _reference = new WeakReference>(r); - } - - public bool IsEmpty - { - get - { - if (_reference == null) - return true; - if (_reference.TryGetTarget(out _)) - return false; - _reference = null; - return true; - } - } - - public bool TryGetTarget([MaybeNullWhen(false)]out IWeakEventSubscriber target) - { - if (_reference == null) - { - target = null!; - return false; - } - return _reference.TryGetTarget(out target); - } - - public bool Equals(IWeakEventSubscriber r) - { - if (_reference == null || r.GetHashCode() != _hashCode) - return false; - return _reference.TryGetTarget(out var target) && target == r; - } - } - private readonly Action _unsubscribe; private readonly WeakHashList> _list = new(); private bool _compactScheduled; @@ -114,7 +60,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event _unsubscribe = ev._subscribe(target, OnEvent); } - void Destroy() + private void Destroy() { if(_destroyed) return; @@ -134,15 +80,15 @@ public class WeakEvent : WeakEvent where TEventArgs : Event ScheduleCompact(); } - void ScheduleCompact() + private void ScheduleCompact() { if(_compactScheduled || _destroyed) return; _compactScheduled = true; Dispatcher.UIThread.Post(_compact, DispatcherPriority.Background); } - - void Compact() + + private void Compact() { if(!_compactScheduled) return; @@ -152,7 +98,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event Destroy(); } - void OnEvent(object? sender, TEventArgs eventArgs) + private void OnEvent(object? sender, TEventArgs eventArgs) { var alive = _list.GetAlive(); if(alive == null) @@ -196,4 +142,4 @@ public class WeakEvent return () => unsubscribe(s, handler); }); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs index 020ba7a6d9..ef143144e6 100644 --- a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs +++ b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs @@ -60,8 +60,7 @@ namespace Avalonia.Utilities private static class SubscriptionTypeStorage where TArgs : EventArgs where TSubscriber : class { - public static readonly ConditionalWeakTable> Subscribers - = new ConditionalWeakTable>(); + public static readonly ConditionalWeakTable> Subscribers = new(); } private class SubscriptionDic : Dictionary> @@ -69,8 +68,7 @@ namespace Avalonia.Utilities { } - private static readonly Dictionary> Accessors - = new Dictionary>(); + private static readonly Dictionary> s_accessors = new(); private class Subscription where T : EventArgs where TSubscriber : class { @@ -81,18 +79,17 @@ namespace Avalonia.Utilities private readonly Delegate _delegate; private Descriptor[] _data = new Descriptor[2]; - private int _count = 0; + private int _count; - delegate void CallerDelegate(TSubscriber s, object sender, T args); - - struct Descriptor + private delegate void CallerDelegate(TSubscriber s, object? sender, T args); + + private struct Descriptor { - public WeakReference Subscriber; - public CallerDelegate Caller; + public WeakReference? Subscriber; + public CallerDelegate? Caller; } - private static Dictionary s_Callers = - new Dictionary(); + private static readonly Dictionary s_callers = new(); public Subscription(SubscriptionDic sdic, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] Type targetType, @@ -101,8 +98,8 @@ namespace Avalonia.Utilities _sdic = sdic; _target = target; _eventName = eventName; - if (!Accessors.TryGetValue(targetType, out var evDic)) - Accessors[targetType] = evDic = new Dictionary(); + if (!s_accessors.TryGetValue(targetType, out var evDic)) + s_accessors[targetType] = evDic = new Dictionary(); if (evDic.TryGetValue(eventName, out var info)) { @@ -123,12 +120,12 @@ namespace Avalonia.Utilities var del = new Action(OnEvent); _delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType!, del.Target); - _info.AddMethod!.Invoke(target, new[] { _delegate }); + _info.AddMethod!.Invoke(target, new object?[] { _delegate }); } - void Destroy() + private void Destroy() { - _info.RemoveMethod!.Invoke(_target, new[] { _delegate }); + _info.RemoveMethod!.Invoke(_target, new object?[] { _delegate }); _sdic.Remove(_eventName); } @@ -146,8 +143,8 @@ namespace Avalonia.Utilities MethodInfo method = s.Method; var subscriber = (TSubscriber)s.Target!; - if (!s_Callers.TryGetValue(method, out var caller)) - s_Callers[method] = caller = + if (!s_callers.TryGetValue(method, out var caller)) + s_callers[method] = caller = (CallerDelegate)Delegate.CreateDelegate(typeof(CallerDelegate), null, method); _data[_count] = new Descriptor { @@ -178,7 +175,7 @@ namespace Avalonia.Utilities } } - void Compact(bool preventDestroy = false) + private void Compact(bool preventDestroy = false) { int empty = -1; for (int c = 0; c < _count; c++) @@ -206,15 +203,15 @@ namespace Avalonia.Utilities Destroy(); } - void OnEvent(object sender, T eventArgs) + private void OnEvent(object? sender, T eventArgs) { var needCompact = false; - for(var c=0; c<_count; c++) + for (var c = 0; c < _count; c++) { - var r = _data[c].Subscriber; + var r = _data[c].Subscriber!; if (r.TryGetTarget(out var sub)) { - _data[c].Caller(sub, sender, eventArgs); + _data[c].Caller!(sub, sender, eventArgs); } else needCompact = true; diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index e6d7492c51..87bb1d3790 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -348,7 +348,7 @@ namespace Avalonia /// public void InvalidateVisual() { - VisualRoot?.Renderer?.AddDirty(this); + VisualRoot?.Renderer.AddDirty(this); } /// @@ -449,7 +449,7 @@ namespace Avalonia protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { base.LogicalChildrenCollectionChanged(sender, e); - VisualRoot?.Renderer?.RecalculateChildren(this); + VisualRoot?.Renderer.RecalculateChildren(this); } /// @@ -477,23 +477,19 @@ namespace Avalonia OnAttachedToVisualTree(e); AttachedToVisualTree?.Invoke(this, e); InvalidateVisual(); - _visualRoot.Renderer?.RecalculateChildren(_visualParent!); + _visualRoot.Renderer.RecalculateChildren(_visualParent!); if (ZIndex != 0 && VisualParent is Visual parent) parent.HasNonUniformZIndexChildren = true; var visualChildren = VisualChildren; + var visualChildrenCount = visualChildren.Count; - if (visualChildren != null) + for (var i = 0; i < visualChildrenCount; i++) { - var visualChildrenCount = visualChildren.Count; - - for (var i = 0; i < visualChildrenCount; i++) + if (visualChildren[i] is { } child) { - if (visualChildren[i] is Visual child) - { - child.OnAttachedToVisualTreeCore(e); - } + child.OnAttachedToVisualTreeCore(e); } } } @@ -540,20 +536,16 @@ namespace Avalonia } DetachedFromVisualTree?.Invoke(this, e); - e.Root?.Renderer?.AddDirty(this); + e.Root.Renderer.AddDirty(this); var visualChildren = VisualChildren; + var visualChildrenCount = visualChildren.Count; - if (visualChildren != null) + for (var i = 0; i < visualChildrenCount; i++) { - var visualChildrenCount = visualChildren.Count; - - for (var i = 0; i < visualChildrenCount; i++) + if (visualChildren[i] is { } child) { - if (visualChildren[i] is Visual child) - { - child.OnDetachedFromVisualTreeCore(e); - } + child.OnDetachedFromVisualTreeCore(e); } } } @@ -659,7 +651,7 @@ namespace Avalonia parentVisual.HasNonUniformZIndexChildren = true; sender?.InvalidateVisual(); - parent?.VisualRoot?.Renderer?.RecalculateChildren(parent); + parent?.VisualRoot?.Renderer.RecalculateChildren(parent); } /// diff --git a/src/Avalonia.Base/VisualTree/VisualExtensions.cs b/src/Avalonia.Base/VisualTree/VisualExtensions.cs index b58db3b276..9e38c6e7f2 100644 --- a/src/Avalonia.Base/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Base/VisualTree/VisualExtensions.cs @@ -46,7 +46,7 @@ namespace Avalonia.VisualTree Visual? v = visual ?? throw new ArgumentNullException(nameof(visual)); var result = 0; - v = v?.VisualParent; + v = v.VisualParent; while (v != null) { @@ -64,17 +64,13 @@ namespace Avalonia.VisualTree /// The first visual. /// The second visual. /// The common ancestor, or null if not found. - public static Visual? FindCommonVisualAncestor(this Visual visual, Visual target) + public static Visual? FindCommonVisualAncestor(this Visual? visual, Visual? target) { - Visual? v = visual ?? throw new ArgumentNullException(nameof(visual)); - - if (target is null) + if (visual is null || target is null) { return null; } - Visual? t = target; - void GoUpwards(ref Visual? node, int count) { for (int i = 0; i < count; ++i) @@ -83,6 +79,9 @@ namespace Avalonia.VisualTree } } + Visual? v = visual; + Visual? t = target; + // 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(v); @@ -144,7 +143,7 @@ namespace Avalonia.VisualTree /// The visual. /// If given visual should be included in search. /// First ancestor of given type. - public static T? FindAncestorOfType(this Visual visual, bool includeSelf = false) where T : class + public static T? FindAncestorOfType(this Visual? visual, bool includeSelf = false) where T : class { if (visual is null) { @@ -173,7 +172,7 @@ namespace Avalonia.VisualTree /// The visual. /// If given visual should be included in search. /// First descendant of given type. - public static T? FindDescendantOfType(this Visual visual, bool includeSelf = false) where T : class + public static T? FindDescendantOfType(this Visual? visual, bool includeSelf = false) where T : class { if (visual is null) { @@ -392,7 +391,7 @@ namespace Avalonia.VisualTree /// True if is an ancestor of ; /// otherwise false. /// - public static bool IsVisualAncestorOf(this Visual visual, Visual target) + public static bool IsVisualAncestorOf(this Visual? visual, Visual? target) { Visual? current = target?.VisualParent; diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index f35124ee0a..91b65a1f72 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -3979,7 +3979,7 @@ namespace Avalonia.Controls { if (focusedObject is Control element) { - parent = element.Parent; + parent = element.VisualParent; if (parent != null) { dataGridWillReceiveRoutedEvent = false; diff --git a/src/Avalonia.Controls.DataGrid/Utils/TreeHelper.cs b/src/Avalonia.Controls.DataGrid/Utils/TreeHelper.cs index f4ba644ae6..6aebf05d6b 100644 --- a/src/Avalonia.Controls.DataGrid/Utils/TreeHelper.cs +++ b/src/Avalonia.Controls.DataGrid/Utils/TreeHelper.cs @@ -36,7 +36,7 @@ namespace Avalonia.Controls.Utils { if (child is Control childElement) { - parent = childElement.Parent; + parent = childElement.VisualParent; } } child = parent; diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index 98885e11ca..55649660f7 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -792,7 +792,7 @@ namespace Avalonia.Controls Control? element = focused as Control; if (element != null) { - parent = element.Parent; + parent = element.VisualParent; } } focused = parent; diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 9627f200df..1ec6f8dabc 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -394,10 +394,10 @@ namespace Avalonia.Controls if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { IsPressed = true; + e.Handled = true; if (ClickMode == ClickMode.Press) { - e.Handled = true; OnClick(); } } @@ -411,11 +411,11 @@ namespace Avalonia.Controls if (IsPressed && e.InitialPressMouseButton == MouseButton.Left) { IsPressed = false; + e.Handled = true; if (ClickMode == ClickMode.Release && this.GetVisualsAt(e.GetPosition(this)).Any(c => this == c || this.IsVisualAncestorOf(c))) { - e.Handled = true; OnClick(); } } diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index ed24c3c7c2..ab7c9948c4 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -2,14 +2,12 @@ using System; using System.Collections.Generic; using System.ComponentModel; using Avalonia.Automation.Peers; -using Avalonia.Controls.Documents; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.LogicalTree; -using Avalonia.Media; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Threading; @@ -211,8 +209,6 @@ namespace Avalonia.Controls remove => RemoveHandler(SizeChangedEvent, value); } - public new Control? Parent => (Control?)base.Parent; - /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index 58afb24b5c..f06c8515ee 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -64,5 +64,23 @@ namespace Avalonia.Controls.Documents internal override void AppendText(StringBuilder stringBuilder) { } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ChildProperty) + { + if(change.OldValue is Control oldChild) + { + LogicalChildren.Remove(oldChild); + } + + if(change.NewValue is Control newChild) + { + LogicalChildren.Add(newChild); + } + } + } } } diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 8b1a307182..80d1677c2f 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -104,6 +104,7 @@ namespace Avalonia.Controls public void UnselectAll() => Selection.Clear(); protected internal override Control CreateContainerForItemOverride() => new ListBoxItem(); + protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is ListBoxItem; /// protected override void OnGotFocus(GotFocusEventArgs e) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index de3aca76d9..4dd868253e 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -553,7 +553,7 @@ namespace Avalonia.Controls.Platform } } - protected static IMenuItem? GetMenuItem(Control? item) + protected static IMenuItem? GetMenuItem(StyledElement? item) { while (true) { diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index e265f4eb6a..d466edeb33 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -48,7 +48,7 @@ namespace Avalonia.Controls.Primitives set { /* Not currently supported in overlay popups */ } } - protected internal override Interactive? InteractiveParent => Parent; + protected internal override Interactive? InteractiveParent => (Interactive?)VisualParent; public void Dispose() => Hide(); diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index c85199a665..3b68cd2ae8 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -723,7 +723,7 @@ namespace Avalonia.Controls.Primitives while (e is object && (!e.Focusable || !e.IsEffectivelyEnabled || !e.IsVisible)) { - e = e.Parent; + e = e.VisualParent as Control; } if (e is object) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 57ec864cad..b3436d4176 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -72,12 +72,12 @@ namespace Avalonia.Controls.Primitives /// /// Popup events are passed to their parent window. This facilitates this. /// - protected internal override Interactive? InteractiveParent => Parent; + protected internal override Interactive? InteractiveParent => (Interactive?)Parent; /// /// Gets the control that is hosting the popup root. /// - Visual? IHostedVisualTreeRoot.Host => Parent; + Visual? IHostedVisualTreeRoot.Host => VisualParent; /// /// Gets the styling parent of the popup root. diff --git a/src/Avalonia.Controls/Primitives/ToggleButton.cs b/src/Avalonia.Controls/Primitives/ToggleButton.cs index dfb436a55e..158c5d875b 100644 --- a/src/Avalonia.Controls/Primitives/ToggleButton.cs +++ b/src/Avalonia.Controls/Primitives/ToggleButton.cs @@ -20,7 +20,7 @@ namespace Avalonia.Controls.Primitives nameof(IsChecked), o => o.IsChecked, (o, v) => o.IsChecked = v, - unsetValue: null, + unsetValue: false, defaultBindingMode: BindingMode.TwoWay); /// diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index 7e0d695264..ce3158b282 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -231,7 +231,7 @@ namespace Avalonia.Controls private void UpdateIndicator() { // Gets the size of the parent indicator container - var barSize = _indicator?.Parent?.Bounds.Size ?? Bounds.Size; + var barSize = _indicator?.VisualParent?.Bounds.Size ?? Bounds.Size; if (_indicator != null) { diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index df9a3eb8f3..ec31470126 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -673,8 +673,6 @@ namespace Avalonia.Controls controlRun.Control is Control control) { VisualChildren.Remove(control); - - LogicalChildren.Remove(control); } } } @@ -693,8 +691,6 @@ namespace Avalonia.Controls { VisualChildren.Add(control); - LogicalChildren.Add(control); - control.Measure(Size.Infinity); } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 676fa1519a..4db71abfa8 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -94,7 +94,6 @@ namespace Avalonia.Controls private readonly IInputManager? _inputManager; private readonly IAccessKeyHandler? _accessKeyHandler; private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler; - private readonly IPlatformRenderInterface? _renderInterface; private readonly IGlobalStyles? _globalStyles; private readonly IGlobalThemeVariantProvider? _applicationThemeHost; private readonly PointerOverPreProcessor? _pointerOverPreProcessor; @@ -136,36 +135,21 @@ namespace Avalonia.Controls /// public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver? dependencyResolver) { - if (impl == null) - { - throw new InvalidOperationException( - "Could not create window implementation: maybe no windowing subsystem was initialized?"); - } - - PlatformImpl = impl; + PlatformImpl = impl ?? throw new InvalidOperationException( + "Could not create window implementation: maybe no windowing subsystem was initialized?"); _actualTransparencyLevel = PlatformImpl.TransparencyLevel; - dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current; + dependencyResolver ??= AvaloniaLocator.Current; _accessKeyHandler = TryGetService(dependencyResolver); _inputManager = TryGetService(dependencyResolver); _keyboardNavigationHandler = TryGetService(dependencyResolver); - _renderInterface = TryGetService(dependencyResolver); _globalStyles = TryGetService(dependencyResolver); _applicationThemeHost = TryGetService(dependencyResolver); Renderer = impl.CreateRenderer(this); - - if (Renderer != null) - { - Renderer.SceneInvalidated += SceneInvalidated; - } - else - { - // Prevent nullable error. - Renderer = null!; - } + Renderer.SceneInvalidated += SceneInvalidated; impl.SetInputRoot(this); @@ -216,7 +200,7 @@ namespace Avalonia.Controls if(impl.TryGetFeature() is {} systemNavigationManager) { - systemNavigationManager.BackRequested += (s, e) => + systemNavigationManager.BackRequested += (_, e) => { e.RoutedEvent = BackRequestedEvent; RaiseEvent(e); @@ -337,7 +321,7 @@ namespace Avalonia.Controls { _layoutManager = CreateLayoutManager(); - if (_layoutManager is LayoutManager typedLayoutManager && Renderer is not null) + if (_layoutManager is LayoutManager typedLayoutManager) { _layoutDiagnosticBridge = new LayoutDiagnosticBridge(Renderer.Diagnostics, typedLayoutManager); _layoutDiagnosticBridge.SetupBridge(); @@ -356,7 +340,7 @@ namespace Avalonia.Controls /// /// Gets the renderer for the window. /// - public IRenderer Renderer { get; private set; } + public IRenderer Renderer { get; } internal PixelPoint? LastPointerPosition => _pointerOverPreProcessor?.LastPosition; @@ -450,7 +434,7 @@ namespace Avalonia.Controls /// The dirty area. protected virtual void HandlePaint(Rect rect) { - Renderer?.Paint(rect); + Renderer.Paint(rect); } /// @@ -468,8 +452,8 @@ namespace Avalonia.Controls _applicationThemeHost.ActualThemeVariantChanged -= GlobalActualThemeVariantChanged; } - Renderer?.Dispose(); - Renderer = null!; + Renderer.SceneInvalidated -= SceneInvalidated; + Renderer.Dispose(); _layoutDiagnosticBridge?.Dispose(); _layoutDiagnosticBridge = null; @@ -488,7 +472,7 @@ namespace Avalonia.Controls OnClosed(EventArgs.Empty); - LayoutManager?.Dispose(); + LayoutManager.Dispose(); } /// @@ -503,7 +487,7 @@ namespace Avalonia.Controls Width = clientSize.Width; Height = clientSize.Height; LayoutManager.ExecuteLayoutPass(); - Renderer?.Resized(clientSize); + Renderer.Resized(clientSize); } /// diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index b1110ece55..ba1b599421 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -573,7 +573,7 @@ namespace Avalonia.Controls return; } - Renderer?.Stop(); + Renderer.Stop(); if (Owner is Window owner) { @@ -721,7 +721,7 @@ namespace Avalonia.Controls SetWindowStartupLocation(owner?.PlatformImpl); PlatformImpl?.Show(ShowActivated, false); - Renderer?.Start(); + Renderer.Start(); OnOpened(EventArgs.Empty); } } @@ -798,7 +798,7 @@ namespace Avalonia.Controls PlatformImpl?.Show(ShowActivated, true); - Renderer?.Start(); + Renderer.Start(); Observable.FromEventPattern( x => Closed += x, diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index aad0482b50..0c9a91148b 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -129,7 +129,7 @@ namespace Avalonia.Controls { using (FreezeVisibilityChangeHandling()) { - Renderer?.Stop(); + Renderer.Stop(); PlatformImpl?.Hide(); IsVisible = false; } @@ -153,7 +153,7 @@ namespace Avalonia.Controls } PlatformImpl?.Show(true, false); - Renderer?.Start(); + Renderer.Start(); OnOpened(EventArgs.Empty); } } @@ -219,7 +219,7 @@ namespace Avalonia.Controls { ClientSize = clientSize; LayoutManager.ExecuteLayoutPass(); - Renderer?.Resized(clientSize); + Renderer.Resized(clientSize); } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs b/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs index 7426c4e2ed..a0ff3a714f 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs @@ -33,7 +33,7 @@ namespace Avalonia.Diagnostics.Controls RendererRoot = application.ApplicationLifetime switch { Lifetimes.IClassicDesktopStyleApplicationLifetime classic => classic.MainWindow?.Renderer, - Lifetimes.ISingleViewApplicationLifetime single => (single.MainView as Visual)?.VisualRoot?.Renderer, + Lifetimes.ISingleViewApplicationLifetime single => single.MainView?.VisualRoot?.Renderer, _ => null }; diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs index 0140281d50..785fd49983 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/EventTreeNode.cs @@ -115,7 +115,7 @@ namespace Avalonia.Diagnostics.ViewModels var link = _currentEvent.EventChain[linkIndex]; link.Handled = true; - _currentEvent.HandledBy = link; + _currentEvent.HandledBy ??= link; } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml index cd2e92914a..f62d8a0b79 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/EventsPageView.xaml @@ -29,6 +29,7 @@ diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 225e846390..68466fe381 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -141,9 +141,7 @@ namespace Avalonia.Headless } public IReadOnlyList GetIntersections(float lowerBound, float upperBound) - { - return null; - } + => Array.Empty(); } class HeadlessGeometryStub : IGeometryImpl diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index f27d94b61a..64c1d0da10 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -119,7 +119,8 @@ namespace Avalonia.Native { if(e.Type == RawPointerEventType.LeftButtonDown) { - var visual = (_inputRoot as Window).Renderer.HitTestFirst(e.Position, _inputRoot as Window, x => + var window = _inputRoot as Window; + var visual = window?.Renderer.HitTestFirst(e.Position, window, x => { if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsEffectivelyVisible)) { diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index dcb20d2a44..ba646c64ee 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -208,6 +208,12 @@ namespace Avalonia.Skia public void DrawLine(IPen pen, Point p1, Point p2) { CheckLease(); + + if (pen is null) + { + return; + } + using (var paint = CreatePaint(_strokePaint, pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y)))) { if (paint.Paint is object) @@ -495,6 +501,12 @@ namespace Avalonia.Skia public void DrawGlyphRun(IBrush foreground, IRef glyphRun) { CheckLease(); + + if (foreground is null) + { + return; + } + using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Item.Size)) { var glyphRunImpl = (GlyphRunImpl)glyphRun.Item; diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs index 24b8fc04b3..446db47d92 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphRunImpl.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Avalonia.Platform; using SharpDX.DirectWrite; @@ -25,8 +26,6 @@ namespace Avalonia.Direct2D1.Media } public IReadOnlyList GetIntersections(float lowerBound, float upperBound) - { - return null; - } + => Array.Empty(); } } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs index 339cf8a334..924e844ec5 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs @@ -78,18 +78,6 @@ namespace Avalonia.Base.UnitTests.Data.Core GC.KeepAlive(data); } - [Fact] - public async Task Should_Coerce_Get_Null_Double_String_To_UnsetValue() - { - var data = new Class1 { StringValue = null }; - var target = new BindingExpression(ExpressionObserver.Create(data, o => o.StringValue), typeof(double)); - var result = await target.Take(1); - - Assert.Equal(AvaloniaProperty.UnsetValue, result); - - GC.KeepAlive(data); - } - [Fact] public void Should_Convert_Set_String_To_Double() { @@ -249,19 +237,6 @@ namespace Avalonia.Base.UnitTests.Data.Core GC.KeepAlive(data); } - [Fact] - public void Should_Coerce_Setting_Null_Double_To_Default_Value() - { - var data = new Class1 { DoubleValue = 5.6 }; - var target = new BindingExpression(ExpressionObserver.Create(data, o => o.DoubleValue), typeof(string)); - - target.OnNext(null); - - Assert.Equal(0, data.DoubleValue); - - GC.KeepAlive(data); - } - [Fact] public void Should_Coerce_Setting_UnsetValue_Double_To_Default_Value() { diff --git a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs index 07d2d672ae..c1468a28e4 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs @@ -29,7 +29,7 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph double height, double scaleX, double scaleY, - double? penThickness, + double penThickness, double expectedX, double expectedY, double expectedWidth, @@ -38,7 +38,7 @@ namespace Avalonia.Base.UnitTests.Rendering.SceneGraph var target = new TestRectangleDrawOperation( new Rect(x, y, width, height), Matrix.CreateScale(scaleX, scaleY), - penThickness.HasValue ? new Pen(Brushes.Black, penThickness.Value) : null); + new Pen(Brushes.Black, penThickness)); Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), target.Bounds); } diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj index 0ddee2ad7a..9ea0482abc 100644 --- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj +++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj @@ -14,7 +14,7 @@ - + diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index 4ff98bdedd..2679d4ce06 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -140,10 +140,9 @@ namespace Avalonia.Controls.UnitTests .Returns>((p, r, f) => r.Bounds.Contains(p) ? new Visual[] { r } : new Visual[0]); - var target = new TestButton() + var target = new TestButton(renderer.Object) { - Bounds = new Rect(0, 0, 100, 100), - Renderer = renderer.Object + Bounds = new Rect(0, 0, 100, 100) }; bool clicked = false; @@ -172,10 +171,9 @@ namespace Avalonia.Controls.UnitTests .Returns>((p, r, f) => r.Bounds.Contains(p) ? new Visual[] { r } : new Visual[0]); - var target = new TestButton() + var target = new TestButton(renderer.Object) { - Bounds = new Rect(0, 0, 100, 100), - Renderer = renderer.Object + Bounds = new Rect(0, 0, 100, 100) }; bool clicked = false; @@ -206,11 +204,10 @@ namespace Avalonia.Controls.UnitTests r.Bounds.Contains(p.Transform(r.RenderTransform.Value.Invert())) ? new Visual[] { r } : new Visual[0]); - var target = new TestButton() + var target = new TestButton(renderer.Object) { Bounds = new Rect(0, 0, 100, 100), - RenderTransform = new TranslateTransform { X = 100, Y = 0 }, - Renderer = renderer.Object + RenderTransform = new TranslateTransform { X = 100, Y = 0 } }; //actual bounds of button should be 100,0,100,100 x -> translated 100 pixels @@ -386,9 +383,10 @@ namespace Avalonia.Controls.UnitTests private class TestButton : Button, IRenderRoot { - public TestButton() + public TestButton(IRenderer renderer) { IsVisible = true; + Renderer = renderer; } public new Rect Bounds @@ -399,7 +397,7 @@ namespace Avalonia.Controls.UnitTests public Size ClientSize => throw new NotImplementedException(); - public IRenderer Renderer { get; set; } + public IRenderer Renderer { get; } public double RenderScaling => throw new NotImplementedException(); diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index d63251c1f5..2644e7184a 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -6,6 +6,7 @@ using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Platform; +using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -20,7 +21,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); var target = new TestTopLevel(impl.Object); Assert.True(((ILogical)target).IsAttachedToLogicalTree); @@ -32,7 +33,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); @@ -46,7 +47,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); @@ -60,7 +61,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); @@ -76,7 +77,7 @@ namespace Avalonia.Controls.UnitTests using (UnitTestApplication.Start(services)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); var target = new TestTopLevel(impl.Object, Mock.Of()); @@ -91,7 +92,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupProperty(x => x.Resized); impl.SetupGet(x => x.RenderScaling).Returns(1); @@ -117,7 +118,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); @@ -133,7 +134,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); @@ -151,7 +152,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); bool raised = false; @@ -169,7 +170,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); var target = new TestTopLevel(impl.Object); @@ -200,7 +201,7 @@ namespace Avalonia.Controls.UnitTests using (UnitTestApplication.Start(services)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); var target = new TestTopLevel(impl.Object); @@ -222,7 +223,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); var target = new TestTopLevel(impl.Object); var child = new TestTopLevel(impl.Object); @@ -240,7 +241,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); var target = new TestTopLevel(impl.Object); var raised = false; @@ -257,7 +258,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupAllProperties(); var layoutManager = new Mock(); @@ -274,7 +275,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockTopLevelImpl(); impl.SetupGet(x => x.RenderScaling).Returns(1); var child = new Border { Classes = { "foo" } }; @@ -317,6 +318,14 @@ namespace Avalonia.Controls.UnitTests }.RegisterInNameScope(scope)); } + private static Mock CreateMockTopLevelImpl() + { + var renderer = new Mock(); + renderer.Setup(r => r.CreateRenderer(It.IsAny())) + .Returns(RendererMocks.CreateRenderer().Object); + return renderer; + } + private class TestTopLevel : TopLevel { private readonly ILayoutManager _layoutManager; diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs index f367112cc0..336aad79da 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs @@ -4,9 +4,7 @@ using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Platform; -using Avalonia.Styling; using Avalonia.UnitTests; -using Moq; using Xunit; using Factory = System.Func, Avalonia.Controls.Window, Avalonia.AvaloniaObject>; @@ -20,7 +18,7 @@ namespace Avalonia.Controls.UnitTests.Utils using (AvaloniaLocator.EnterScope()) { AvaloniaLocator.CurrentMutable - .Bind().ToConstant(new WindowingPlatformMock()); + .Bind().ToConstant(new MockWindowingPlatform()); var gesture1 = new KeyGesture(Key.A, KeyModifiers.Control); var gesture2 = new KeyGesture(Key.B, KeyModifiers.Control); @@ -64,7 +62,7 @@ namespace Avalonia.Controls.UnitTests.Utils var commandResult = 0; var expectedParameter = 1; AvaloniaLocator.CurrentMutable - .Bind().ToConstant(new WindowingPlatformMock()); + .Bind().ToConstant(new MockWindowingPlatform()); var gesture = new KeyGesture(Key.A, KeyModifiers.Control); @@ -106,7 +104,7 @@ namespace Avalonia.Controls.UnitTests.Utils var target = new KeyboardDevice(); var isExecuted = false; AvaloniaLocator.CurrentMutable - .Bind().ToConstant(new WindowingPlatformMock()); + .Bind().ToConstant(new MockWindowingPlatform()); var gesture = new KeyGesture(Key.A, KeyModifiers.Control); @@ -146,7 +144,7 @@ namespace Avalonia.Controls.UnitTests.Utils var target = new KeyboardDevice(); var clickExecutedCount = 0; AvaloniaLocator.CurrentMutable - .Bind().ToConstant(new WindowingPlatformMock()); + .Bind().ToConstant(new MockWindowingPlatform()); var gesture = new KeyGesture(Key.A, KeyModifiers.Control); @@ -199,7 +197,7 @@ namespace Avalonia.Controls.UnitTests.Utils var clickExecutedCount = 0; var commandExecutedCount = 0; AvaloniaLocator.CurrentMutable - .Bind().ToConstant(new WindowingPlatformMock()); + .Bind().ToConstant(new MockWindowingPlatform()); var gesture = new KeyGesture(Key.A, KeyModifiers.Control); diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index a10b1324d6..d65fa06183 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockWindowBaseImpl(); var target = new TestWindowBase(impl.Object); target.Activate(); @@ -36,7 +36,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockWindowBaseImpl(); impl.SetupAllProperties(); bool raised = false; @@ -55,7 +55,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var impl = new Mock(); + var impl = CreateMockWindowBaseImpl(); impl.SetupAllProperties(); bool raised = false; @@ -241,6 +241,14 @@ namespace Avalonia.Controls.UnitTests }.RegisterInNameScope(scope)); } + private static Mock CreateMockWindowBaseImpl() + { + var renderer = new Mock(); + renderer.Setup(r => r.CreateRenderer(It.IsAny())) + .Returns(RendererMocks.CreateRenderer().Object); + return renderer; + } + private class TestWindowBase : WindowBase { public bool IsClosed { get; private set; } diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index 014174990e..cada2bfa6f 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -15,6 +15,8 @@ namespace Avalonia.Controls.UnitTests public void Setting_Title_Should_Set_Impl_Title() { var windowImpl = new Mock(); + windowImpl.Setup(r => r.CreateRenderer(It.IsAny())) + .Returns(RendererMocks.CreateRenderer().Object); var windowingPlatform = new MockWindowingPlatform(() => windowImpl.Object); using (UnitTestApplication.Start(new TestServices(windowingPlatform: windowingPlatform))) diff --git a/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs b/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs deleted file mode 100644 index e8471d41fb..0000000000 --- a/tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Moq; -using Avalonia.Platform; - -namespace Avalonia.Controls.UnitTests -{ - public class WindowingPlatformMock : IWindowingPlatform - { - private readonly Func _windowImpl; - private readonly Func _popupImpl; - - public WindowingPlatformMock(Func windowImpl = null, Func popupImpl = null ) - { - _windowImpl = windowImpl; - _popupImpl = popupImpl; - } - - public IWindowImpl CreateWindow() - { - return _windowImpl?.Invoke() ?? Mock.Of(x => x.RenderScaling == 1); - } - - public IWindowImpl CreateEmbeddableWindow() - { - throw new NotImplementedException(); - } - - public ITrayIconImpl CreateTrayIcon() - { - return null; - } - - public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of(x => x.RenderScaling == 1); - } -} diff --git a/tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj b/tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj index 3ff91139f1..5de2b85569 100644 --- a/tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj +++ b/tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 15815c81b6..c3d9aa0622 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -1,6 +1,6 @@  - net461 + net462 diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs index 3ba8e8354d..c312a71d44 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs @@ -648,16 +648,69 @@ namespace Avalonia.Markup.UnitTests.Data }; } + [Fact] + public void Binding_Producing_Default_Value_Should_Result_In_Correct_Priority() + { + var defaultValue = StyledPropertyClass.NullableDoubleProperty.GetDefaultValue(typeof(StyledPropertyClass)); + + var vm = new NullableValuesViewModel() { NullableDouble = defaultValue }; + var target = new StyledPropertyClass(); + + target.Bind(StyledPropertyClass.NullableDoubleProperty, new Binding(nameof(NullableValuesViewModel.NullableDouble)) { Source = vm }); + + Assert.Equal(BindingPriority.LocalValue, target.GetDiagnosticInternal(StyledPropertyClass.NullableDoubleProperty).Priority); + Assert.Equal(defaultValue, target.GetValue(StyledPropertyClass.NullableDoubleProperty)); + } + + [Fact] + public void Binding_Non_Nullable_ValueType_To_Null_Reverts_To_Default_Value() + { + var source = new NullableValuesViewModel { NullableDouble = 42 }; + var target = new StyledPropertyClass(); + var binding = new Binding(nameof(source.NullableDouble)) { Source = source }; + + target.Bind(StyledPropertyClass.DoubleValueProperty, binding); + Assert.Equal(42, target.DoubleValue); + + source.NullableDouble = null; + + Assert.Equal(12.3, target.DoubleValue); + } + + [Fact] + public void Binding_Nullable_ValueType_To_Null_Sets_Value_To_Null() + { + var source = new NullableValuesViewModel { NullableDouble = 42 }; + var target = new StyledPropertyClass(); + var binding = new Binding(nameof(source.NullableDouble)) { Source = source }; + + target.Bind(StyledPropertyClass.NullableDoubleProperty, binding); + Assert.Equal(42, target.NullableDouble); + + source.NullableDouble = null; + + Assert.Null(target.NullableDouble); + } + private class StyledPropertyClass : AvaloniaObject { public static readonly StyledProperty DoubleValueProperty = - AvaloniaProperty.Register(nameof(DoubleValue)); + AvaloniaProperty.Register(nameof(DoubleValue), 12.3); public double DoubleValue { get { return GetValue(DoubleValueProperty); } set { SetValue(DoubleValueProperty, value); } } + + public static StyledProperty NullableDoubleProperty = + AvaloniaProperty.Register(nameof(NullableDoubleProperty), -1); + + public double? NullableDouble + { + get => GetValue(NullableDoubleProperty); + set => SetValue(NullableDoubleProperty, value); + } } private class DirectPropertyClass : AvaloniaObject @@ -676,6 +729,21 @@ namespace Avalonia.Markup.UnitTests.Data } } + private class NullableValuesViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + private double? _nullableDouble; + public double? NullableDouble + { + get => _nullableDouble; set + { + _nullableDouble = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NullableDouble))); + } + } + } + private class TestStackOverflowViewModel : INotifyPropertyChanged { public int SetterInvokedCount { get; private set; } diff --git a/tests/Avalonia.UnitTests/MockGlyphRun.cs b/tests/Avalonia.UnitTests/MockGlyphRun.cs index 477f34565f..0319803a5e 100644 --- a/tests/Avalonia.UnitTests/MockGlyphRun.cs +++ b/tests/Avalonia.UnitTests/MockGlyphRun.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Avalonia.Media.TextFormatting; using Avalonia.Platform; @@ -24,12 +25,9 @@ namespace Avalonia.UnitTests public void Dispose() { - } public IReadOnlyList GetIntersections(float lowerBound, float upperBound) - { - return null; - } + => Array.Empty(); } } diff --git a/tests/Avalonia.UnitTests/TestTemplatedRoot.cs b/tests/Avalonia.UnitTests/TestTemplatedRoot.cs deleted file mode 100644 index 38ab3c3c5d..0000000000 --- a/tests/Avalonia.UnitTests/TestTemplatedRoot.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using Avalonia.Controls; -using Avalonia.Controls.Presenters; -using Avalonia.Controls.Templates; -using Avalonia.Layout; -using Avalonia.LogicalTree; -using Avalonia.Platform; -using Avalonia.Rendering; -using Avalonia.Styling; - -namespace Avalonia.UnitTests -{ - public class TestTemplatedRoot : ContentControl, ILayoutRoot, IRenderRoot, ILogicalRoot - { - private readonly NameScope _nameScope = new NameScope(); - - public TestTemplatedRoot() - { - LayoutManager = new LayoutManager(this); - Template = new FuncControlTemplate((x, scope) => new ContentPresenter - { - Name = "PART_ContentPresenter", - }.RegisterInNameScope(scope)); - } - - public Size ClientSize => new Size(100, 100); - - public Size MaxClientSize => Size.Infinity; - - public double LayoutScaling => 1; - - public ILayoutManager LayoutManager { get; set; } - - public double RenderScaling => 1; - - public IRenderTarget RenderTarget => null; - - public IRenderer Renderer => null; - - public IRenderTarget CreateRenderTarget() - { - throw new NotImplementedException(); - } - - public void Invalidate(Rect rect) - { - throw new NotImplementedException(); - } - - public Point PointToClient(PixelPoint p) => p.ToPoint(1); - - public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1); - } -}