From cc93b6cb861e96075620e1067184ee2657ab47b2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 15:37:53 +0200 Subject: [PATCH 001/173] feat: Enable rule CA1822 --- .editorconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.editorconfig b/.editorconfig index 337760636b..bcc1b4e011 100644 --- a/.editorconfig +++ b/.editorconfig @@ -141,6 +141,8 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme dotnet_diagnostic.CA1802.severity = warning # CA1825: Avoid zero-length array allocations dotnet_diagnostic.CA1825.severity = warning +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = warning # Wrapping preferences csharp_wrap_before_ternary_opsigns = false From 8724514a73e901d61e5fc622e4e21cadbe3d54d3 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:04:53 +0200 Subject: [PATCH 002/173] feat(ControlCatalog): Address rule CA1822 --- .../Pages/AutoCompleteBoxPage.xaml.cs | 8 +++---- .../Pages/CompositionPage.axaml.cs | 4 ++-- .../ControlCatalog/Pages/ImagePage.xaml.cs | 4 ++-- .../ControlCatalog/Pages/OpenGlPage.xaml.cs | 22 +++++++++---------- samples/ControlCatalog/Pages/ScreenPage.cs | 12 +++++----- .../Pages/TabControlPage.xaml.cs | 16 +++++++------- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs index bc18327f12..15021b89c6 100644 --- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs @@ -32,7 +32,7 @@ namespace ControlCatalog.Pages } } - private StateData[] BuildAllStates() + private static StateData[] BuildAllStates() { return new StateData[] { @@ -90,7 +90,7 @@ namespace ControlCatalog.Pages } public StateData[] States { get; private set; } - private LinkedList[] BuildAllSentences() + private static LinkedList[] BuildAllSentences() { return new string[] { @@ -108,8 +108,8 @@ namespace ControlCatalog.Pages { this.InitializeComponent(); - States = BuildAllStates(); - Sentences = BuildAllSentences(); + States = AutoCompleteBoxPage.BuildAllStates(); + Sentences = AutoCompleteBoxPage.BuildAllSentences(); foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox")) { diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs index 61e0ed5acb..877c183a22 100644 --- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs +++ b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs @@ -22,10 +22,10 @@ public partial class CompositionPage : UserControl protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - this.Get("Items").Items = CreateColorItems(); + this.Get("Items").Items = CompositionPage.CreateColorItems(); } - private List CreateColorItems() + private static List CreateColorItems() { var list = new List(); diff --git a/samples/ControlCatalog/Pages/ImagePage.xaml.cs b/samples/ControlCatalog/Pages/ImagePage.xaml.cs index 45043aa5af..a520ee9b46 100644 --- a/samples/ControlCatalog/Pages/ImagePage.xaml.cs +++ b/samples/ControlCatalog/Pages/ImagePage.xaml.cs @@ -52,13 +52,13 @@ namespace ControlCatalog.Pages var comboxBox = (ComboBox)sender; if (_croppedImage.Source is CroppedBitmap croppedBitmap) { - croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex); + croppedBitmap.SourceRect = ImagePage.GetCropRect(comboxBox.SelectedIndex); } } } - private PixelRect GetCropRect(int index) + private static PixelRect GetCropRect(int index) { var bitmapWidth = 640; var bitmapHeight = 426; diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index a126fbefe5..2f636f88cf 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -247,7 +247,7 @@ namespace ControlCatalog.Pages } - private void CheckError(GlInterface gl) + private static void CheckError(GlInterface gl) { int err; while ((err = gl.GetError()) != GL_NO_ERROR) @@ -256,7 +256,7 @@ namespace ControlCatalog.Pages protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) { - CheckError(GL); + OpenGlPageControl.CheckError(GL); Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}"; @@ -277,13 +277,13 @@ namespace ControlCatalog.Pages GL.BindAttribLocationString(_shaderProgram, positionLocation, "aPos"); GL.BindAttribLocationString(_shaderProgram, normalLocation, "aNormal"); Console.WriteLine(GL.LinkProgramAndGetError(_shaderProgram)); - CheckError(GL); + OpenGlPageControl.CheckError(GL); // Create the vertex buffer object (VBO) for the vertex data. _vertexBufferObject = GL.GenBuffer(); // Bind the VBO and copy the vertex data into it. GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); - CheckError(GL); + OpenGlPageControl.CheckError(GL); var vertexSize = Marshal.SizeOf(); fixed (void* pdata = _points) GL.BufferData(GL_ARRAY_BUFFER, new IntPtr(_points.Length * vertexSize), @@ -291,21 +291,21 @@ namespace ControlCatalog.Pages _indexBufferObject = GL.GenBuffer(); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); - CheckError(GL); + OpenGlPageControl.CheckError(GL); fixed (void* pdata = _indices) GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), GL_STATIC_DRAW); - CheckError(GL); + OpenGlPageControl.CheckError(GL); _vertexArrayObject = GL.GenVertexArray(); GL.BindVertexArray(_vertexArrayObject); - CheckError(GL); + OpenGlPageControl.CheckError(GL); GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT, 0, vertexSize, IntPtr.Zero); GL.VertexAttribPointer(normalLocation, 3, GL_FLOAT, 0, vertexSize, new IntPtr(12)); GL.EnableVertexAttribArray(positionLocation); GL.EnableVertexAttribArray(normalLocation); - CheckError(GL); + OpenGlPageControl.CheckError(GL); } @@ -339,7 +339,7 @@ namespace ControlCatalog.Pages GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); GL.BindVertexArray(_vertexArrayObject); GL.UseProgram(_shaderProgram); - CheckError(GL); + OpenGlPageControl.CheckError(GL); var projection = Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height), 0.01f, 1000); @@ -361,10 +361,10 @@ namespace ControlCatalog.Pages GL.Uniform1f(minYLoc, _minY); GL.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds); GL.Uniform1f(discoLoc, _disco); - CheckError(GL); + OpenGlPageControl.CheckError(GL); GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); - CheckError(GL); + OpenGlPageControl.CheckError(GL); if (_disco > 0.01) Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); } diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 823f59e030..841b347d78 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -55,21 +55,21 @@ namespace ControlCatalog.Pages context.DrawRectangle(p, workingAreaRect); - var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"); + var formattedText = ScreenPage.CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height)); formattedText = - CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"); + ScreenPage.CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20)); - formattedText = CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%"); + formattedText = ScreenPage.CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%"); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40)); - formattedText = CreateFormattedText($"Primary: {screen.Primary}"); + formattedText = ScreenPage.CreateFormattedText($"Primary: {screen.Primary}"); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60)); formattedText = - CreateFormattedText( + ScreenPage.CreateFormattedText( $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}"); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 80)); } @@ -77,7 +77,7 @@ namespace ControlCatalog.Pages context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); } - private FormattedText CreateFormattedText(string textToFormat) + private static FormattedText CreateFormattedText(string textToFormat) { return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface.Default, 12, Brushes.Green); diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs index bd0214c72e..2cc4587dce 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs @@ -14,6 +14,12 @@ namespace ControlCatalog.Pages public class TabControlPage : UserControl { + private static IBitmap LoadBitmap(string uri) + { + var assets = AvaloniaLocator.Current!.GetService()!; + return new Bitmap(assets.Open(new Uri(uri))); + } + public TabControlPage() { InitializeComponent(); @@ -26,13 +32,13 @@ namespace ControlCatalog.Pages { Header = "Arch", Text = "This is the first templated tab page.", - Image = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"), + Image = TabControlPage.LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"), }, new TabItemViewModel { Header = "Leaf", Text = "This is the second templated tab page.", - Image = LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"), + Image = TabControlPage.LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"), }, new TabItemViewModel { @@ -50,12 +56,6 @@ namespace ControlCatalog.Pages AvaloniaXamlLoader.Load(this); } - private IBitmap LoadBitmap(string uri) - { - var assets = AvaloniaLocator.Current!.GetService()!; - return new Bitmap(assets.Open(new Uri(uri))); - } - private class PageViewModel : ViewModelBase { private Dock _tabPlacement; From 28c033982b17b3f9d9730eca946377207d352458 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:05:43 +0200 Subject: [PATCH 003/173] feat(Android): Address rule CA1822 --- .../Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs | 2 +- .../Avalonia.Android/Platform/Storage/AndroidStorageItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 4cae700c0a..9de9841266 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -30,7 +30,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers return DispatchKeyEventInternal(e, out callBase); } - string UnicodeTextInput(KeyEvent keyEvent) + static string UnicodeTextInput(KeyEvent keyEvent) { return keyEvent.Action == KeyEventActions.Multiple && keyEvent.RepeatCount == 0 diff --git a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs index a9b2e16d43..078f70db60 100644 --- a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs +++ b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs @@ -178,7 +178,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF return false; } - private Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput) + private static Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput) { var mimeTypes = context.ContentResolver?.GetStreamTypes(uri, FilePickerFileTypes.All.MimeTypes![0]); if (mimeTypes?.Length >= 1) From 93998f2a03d214248ca5384b51ca815527d4565c Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:07:00 +0200 Subject: [PATCH 004/173] feat(Base): Address rule CA1822 --- .../Animators/GradientBrushAnimator.cs | 8 ++++---- src/Avalonia.Base/Animation/KeySpline.cs | 8 ++++---- src/Avalonia.Base/Controls/Classes.cs | 18 +++++++++--------- src/Avalonia.Base/Data/Core/ExpressionNode.cs | 4 ++-- .../Parsers/ExpressionVisitorNodeBuilder.cs | 6 +++--- .../Plugins/DataAnnotationsValidationPlugin.cs | 4 ++-- .../Data/Core/Plugins/IndeiValidationPlugin.cs | 4 ++-- .../Data/Core/Plugins/TaskStreamPlugin.cs | 6 +++--- src/Avalonia.Base/Input/DragDropDevice.cs | 16 ++++++++-------- src/Avalonia.Base/Input/KeyGesture.cs | 4 ++-- src/Avalonia.Base/Input/MouseDevice.cs | 6 +++--- src/Avalonia.Base/Input/PenDevice.cs | 4 ++-- src/Avalonia.Base/Input/Pointer.cs | 4 ++-- src/Avalonia.Base/Input/TouchDevice.cs | 8 ++++---- src/Avalonia.Base/Layout/AttachedLayout.cs | 10 +++++----- src/Avalonia.Base/Layout/StackLayout.cs | 8 ++++---- src/Avalonia.Base/Layout/UniformGridLayout.cs | 8 ++++---- src/Avalonia.Base/Platform/AssetLoader.cs | 8 ++++---- .../Animations/KeyFrameAnimationInstance.cs | 12 ++++++------ .../Composition/CompositingRenderer.cs | 6 +++--- .../Rendering/Composition/CompositionTarget.cs | 6 +++--- .../Drawing/CompositionDrawingContext.cs | 14 +++++++------- .../Rendering/DeferredRenderer.cs | 6 +++--- .../Rendering/SceneGraph/SceneBuilder.cs | 6 +++--- 24 files changed, 92 insertions(+), 92 deletions(-) diff --git a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs index 4727ea1bfb..f469ea5652 100644 --- a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs @@ -28,7 +28,7 @@ namespace Avalonia.Animation.Animators { case IRadialGradientBrush oldRadial when newValue is IRadialGradientBrush newRadial: return new ImmutableRadialGradientBrush( - InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + GradientBrushAnimator.InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), @@ -39,7 +39,7 @@ namespace Avalonia.Animation.Animators case IConicGradientBrush oldConic when newValue is IConicGradientBrush newConic: return new ImmutableConicGradientBrush( - InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + GradientBrushAnimator.InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), @@ -49,7 +49,7 @@ namespace Avalonia.Animation.Animators case ILinearGradientBrush oldLinear when newValue is ILinearGradientBrush newLinear: return new ImmutableLinearGradientBrush( - InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + GradientBrushAnimator.InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), @@ -72,7 +72,7 @@ namespace Avalonia.Animation.Animators return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); } - private IReadOnlyList InterpolateStops(double progress, IReadOnlyList oldValue, IReadOnlyList newValue) + private static IReadOnlyList InterpolateStops(double progress, IReadOnlyList oldValue, IReadOnlyList newValue) { var resultCount = Math.Max(oldValue.Count, newValue.Count); var stops = new ImmutableGradientStop[resultCount]; diff --git a/src/Avalonia.Base/Animation/KeySpline.cs b/src/Avalonia.Base/Animation/KeySpline.cs index a6e9769186..b33cd6b881 100644 --- a/src/Avalonia.Base/Animation/KeySpline.cs +++ b/src/Avalonia.Base/Animation/KeySpline.cs @@ -98,7 +98,7 @@ namespace Avalonia.Animation get => _controlPointX1; set { - if (IsValidXValue(value)) + if (KeySpline.IsValidXValue(value)) { _controlPointX1 = value; _isDirty = true; @@ -131,7 +131,7 @@ namespace Avalonia.Animation get => _controlPointX2; set { - if (IsValidXValue(value)) + if (KeySpline.IsValidXValue(value)) { _controlPointX2 = value; _isDirty = true; @@ -188,7 +188,7 @@ namespace Avalonia.Animation /// acceptable range; false otherwise. public bool IsValid() { - return IsValidXValue(_controlPointX1) && IsValidXValue(_controlPointX2); + return KeySpline.IsValidXValue(_controlPointX1) && KeySpline.IsValidXValue(_controlPointX2); } /// @@ -196,7 +196,7 @@ namespace Avalonia.Animation /// /// /// - private bool IsValidXValue(double value) + private static bool IsValidXValue(double value) { return value >= 0.0 && value <= 1.0; } diff --git a/src/Avalonia.Base/Controls/Classes.cs b/src/Avalonia.Base/Controls/Classes.cs index c3d3fbca46..100c2b2a8f 100644 --- a/src/Avalonia.Base/Controls/Classes.cs +++ b/src/Avalonia.Base/Controls/Classes.cs @@ -63,7 +63,7 @@ namespace Avalonia.Controls /// public override void Add(string name) { - ThrowIfPseudoclass(name, "added"); + Classes.ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -87,7 +87,7 @@ namespace Avalonia.Controls foreach (var name in names) { - ThrowIfPseudoclass(name, "added"); + Classes.ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -127,7 +127,7 @@ namespace Avalonia.Controls /// public override void Insert(int index, string name) { - ThrowIfPseudoclass(name, "added"); + Classes.ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -152,7 +152,7 @@ namespace Avalonia.Controls foreach (var name in names) { - ThrowIfPseudoclass(name, "added"); + Classes.ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -180,7 +180,7 @@ namespace Avalonia.Controls /// public override bool Remove(string name) { - ThrowIfPseudoclass(name, "removed"); + Classes.ThrowIfPseudoclass(name, "removed"); if (base.Remove(name)) { @@ -206,7 +206,7 @@ namespace Avalonia.Controls foreach (var name in names) { - ThrowIfPseudoclass(name, "removed"); + Classes.ThrowIfPseudoclass(name, "removed"); toRemove ??= new List(); @@ -232,7 +232,7 @@ namespace Avalonia.Controls public override void RemoveAt(int index) { var name = this[index]; - ThrowIfPseudoclass(name, "removed"); + Classes.ThrowIfPseudoclass(name, "removed"); base.RemoveAt(index); NotifyChanged(); } @@ -258,7 +258,7 @@ namespace Avalonia.Controls foreach (var name in source) { - ThrowIfPseudoclass(name, "added"); + Classes.ThrowIfPseudoclass(name, "added"); } foreach (var name in this) @@ -320,7 +320,7 @@ namespace Avalonia.Controls listener.Changed(); } - private void ThrowIfPseudoclass(string name, string operation) + private static void ThrowIfPseudoclass(string name, string operation) { if (name.StartsWith(":")) { diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 4f755ff140..d1bc60541c 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -138,7 +138,7 @@ namespace Avalonia.Data.Core if (target == null) { - ValueChanged(TargetNullNotification()); + ValueChanged(ExpressionNode.TargetNullNotification()); _listening = false; } else if (target != AvaloniaProperty.UnsetValue) @@ -159,7 +159,7 @@ namespace Avalonia.Data.Core _listening = false; } - private BindingNotification TargetNullNotification() + private static BindingNotification TargetNullNotification() { return new BindingNotification( new MarkupBindingChainException("Null value"), diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs index 1e82214d76..9b9ddf2183 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs +++ b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs @@ -70,7 +70,7 @@ namespace Avalonia.Data.Core.Parsers if (node.Indexer == AvaloniaObjectIndexer) { - var property = GetArgumentExpressionValue(node.Arguments[0]); + var property = ExpressionVisitorNodeBuilder.GetArgumentExpressionValue(node.Arguments[0]); Nodes.Add(new AvaloniaPropertyAccessorNode(property, _enableDataValidation)); } else @@ -81,7 +81,7 @@ namespace Avalonia.Data.Core.Parsers return node; } - private T GetArgumentExpressionValue(Expression expr) + private static T GetArgumentExpressionValue(Expression expr) { try { @@ -162,7 +162,7 @@ namespace Avalonia.Data.Core.Parsers if (node.Method == CreateDelegateMethod) { var visited = Visit(node.Arguments[1]); - Nodes.Add(new PropertyAccessorNode(GetArgumentExpressionValue(node.Object!).Name, _enableDataValidation)); + Nodes.Add(new PropertyAccessorNode(ExpressionVisitorNodeBuilder.GetArgumentExpressionValue(node.Object!).Name, _enableDataValidation)); return node; } else if (node.Method.Name == StreamBindingExtensions.StreamBindingName || node.Method.Name.StartsWith(StreamBindingExtensions.StreamBindingName + '`')) diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs index 361d68dc81..54d5b5ac28 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs @@ -57,13 +57,13 @@ namespace Avalonia.Data.Core.Plugins else { base.InnerValueChanged(new BindingNotification( - CreateException(errors), + Accessor.CreateException(errors), BindingErrorType.DataValidationError, value)); } } - private Exception CreateException(IList errors) + private static Exception CreateException(IList errors) { if (errors.Count == 1) { diff --git a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs index 1e7a0d5c8f..e45170ff7e 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs @@ -92,7 +92,7 @@ namespace Avalonia.Data.Core.Plugins if (errors?.Count > 0) { return new BindingNotification( - GenerateException(errors), + Validator.GenerateException(errors), BindingErrorType.DataValidationError, value); } @@ -108,7 +108,7 @@ namespace Avalonia.Data.Core.Plugins return target; } - private Exception GenerateException(IList errors) + private static Exception GenerateException(IList errors) { if (errors.Count == 1) { diff --git a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs index 377ea9f275..b25d592597 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs @@ -44,11 +44,11 @@ namespace Avalonia.Data.Core.Plugins { case TaskStatus.RanToCompletion: case TaskStatus.Faulted: - return HandleCompleted(task); + return TaskStreamPlugin.HandleCompleted(task); default: var subject = new Subject(); task.ContinueWith( - x => HandleCompleted(task).Subscribe(subject), + x => TaskStreamPlugin.HandleCompleted(task).Subscribe(subject), TaskScheduler.FromCurrentSynchronizationContext()) .ConfigureAwait(false); return subject; @@ -59,7 +59,7 @@ namespace Avalonia.Data.Core.Plugins return Observable.Empty(); } - private IObservable HandleCompleted(Task task) + private static IObservable HandleCompleted(Task task) { var resultProperty = task.GetType().GetRuntimeProperty("Result"); diff --git a/src/Avalonia.Base/Input/DragDropDevice.cs b/src/Avalonia.Base/Input/DragDropDevice.cs index 30a08eda17..16ff428d69 100644 --- a/src/Avalonia.Base/Input/DragDropDevice.cs +++ b/src/Avalonia.Base/Input/DragDropDevice.cs @@ -11,7 +11,7 @@ namespace Avalonia.Input private Interactive? _lastTarget = null; - private Interactive? GetTarget(IInputRoot root, Point local) + private static Interactive? GetTarget(IInputRoot root, Point local) { var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType()?.FirstOrDefault(); if (target != null && DragDrop.GetAllowDrop(target)) @@ -19,7 +19,7 @@ namespace Avalonia.Input return null; } - private DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers) + private static DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers) { if (target == null) return DragDropEffects.None; @@ -40,22 +40,22 @@ namespace Avalonia.Input private DragDropEffects DragEnter(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { - _lastTarget = GetTarget(inputRoot, point); - return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); + _lastTarget = DragDropDevice.GetTarget(inputRoot, point); + return DragDropDevice.RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } private DragDropEffects DragOver(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { - var target = GetTarget(inputRoot, point); + var target = DragDropDevice.GetTarget(inputRoot, point); if (target == _lastTarget) - return RaiseDragEvent(target, inputRoot, point, DragDrop.DragOverEvent, effects, data, modifiers); + return DragDropDevice.RaiseDragEvent(target, inputRoot, point, DragDrop.DragOverEvent, effects, data, modifiers); try { if (_lastTarget != null) _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); - return RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); + return DragDropDevice.RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } finally { @@ -81,7 +81,7 @@ namespace Avalonia.Input { try { - return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DropEvent, effects, data, modifiers); + return DragDropDevice.RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DropEvent, effects, data, modifiers); } finally { diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 1a6372d346..7f4f69590a 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -138,7 +138,7 @@ namespace Avalonia.Input public bool Matches(KeyEventArgs keyEvent) => keyEvent != null && keyEvent.KeyModifiers == KeyModifiers && - ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key); + KeyGesture.ResolveNumPadOperationKey(keyEvent.Key) == KeyGesture.ResolveNumPadOperationKey(Key); // TODO: Move that to external key parser private static Key ParseKey(string key) @@ -166,7 +166,7 @@ namespace Avalonia.Input return EnumHelper.Parse(modifier.ToString(), true); } - private Key ResolveNumPadOperationKey(Key key) + private static Key ResolveNumPadOperationKey(Key key) { switch (key) { diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 055c9cf1fd..6fb34efdb5 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -34,7 +34,7 @@ namespace Avalonia.Input ProcessRawEvent(margs); } - int ButtonCount(PointerPointProperties props) + static int ButtonCount(PointerPointProperties props) { var rv = 0; if (props.IsLeftButtonPressed) @@ -71,7 +71,7 @@ namespace Avalonia.Input case RawPointerEventType.MiddleButtonDown: case RawPointerEventType.XButton1Down: case RawPointerEventType.XButton2Down: - if (ButtonCount(props) > 1) + if (MouseDevice.ButtonCount(props) > 1) e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); @@ -81,7 +81,7 @@ namespace Avalonia.Input case RawPointerEventType.MiddleButtonUp: case RawPointerEventType.XButton1Up: case RawPointerEventType.XButton2Up: - if (ButtonCount(props) != 0) + if (MouseDevice.ButtonCount(props) != 0) e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index f5f0e90a45..876be42be8 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -56,7 +56,7 @@ namespace Avalonia.Input e.Handled = PenUp(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Move: - e.Handled = PenMove(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult, e.IntermediatePoints); + e.Handled = PenDevice.PenMove(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult, e.IntermediatePoints); break; } @@ -98,7 +98,7 @@ namespace Avalonia.Input return false; } - private bool PenMove(Pointer pointer, ulong timestamp, + private static bool PenMove(Pointer pointer, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers, IInputElement? hitTest, Lazy?>? intermediatePoints) diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index 3012f07f6a..be93d9d6b8 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -19,7 +19,7 @@ namespace Avalonia.Input public int Id { get; } - IInputElement? FindCommonParent(IInputElement? control1, IInputElement? control2) + static IInputElement? FindCommonParent(IInputElement? control1, IInputElement? control2) { if (control1 == null || control2 == null) return null; @@ -41,7 +41,7 @@ namespace Avalonia.Input PlatformCapture(control); if (oldCapture != null) { - var commonParent = FindCommonParent(control, oldCapture); + var commonParent = Pointer.FindCommonParent(control, oldCapture); foreach (var notifyTarget in oldCapture.GetSelfAndVisualAncestors().OfType()) { if (notifyTarget == commonParent) diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index 1d5b1d6bbf..b709b7d0cd 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -20,7 +20,7 @@ namespace Avalonia.Input private Rect _lastClickRect; private ulong _lastClickTime; - RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) + static RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) { var rv = modifiers &= RawInputModifiers.KeyboardMask; if (isLeftButtonDown) @@ -73,7 +73,7 @@ namespace Avalonia.Input target.RaiseEvent(new PointerPressedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), + new PointerPointProperties(TouchDevice.GetModifiers(args.InputModifiers, true), updateKind), keyModifier, _clickCount)); } @@ -84,7 +84,7 @@ namespace Avalonia.Input { target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), + new PointerPointProperties(TouchDevice.GetModifiers(args.InputModifiers, false), updateKind), keyModifier, MouseButton.Left)); } } @@ -100,7 +100,7 @@ namespace Avalonia.Input { target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), + new PointerPointProperties(TouchDevice.GetModifiers(args.InputModifiers, true), updateKind), keyModifier, args.IntermediatePoints)); } } diff --git a/src/Avalonia.Base/Layout/AttachedLayout.cs b/src/Avalonia.Base/Layout/AttachedLayout.cs index ece8bbe805..594fc04842 100644 --- a/src/Avalonia.Base/Layout/AttachedLayout.cs +++ b/src/Avalonia.Base/Layout/AttachedLayout.cs @@ -67,7 +67,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = GetVirtualizingLayoutContext(context); + var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); virtualizingLayout.InitializeForContextCore(virtualizingContext); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -92,7 +92,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = GetVirtualizingLayoutContext(context); + var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); virtualizingLayout.UninitializeForContextCore(virtualizingContext); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -126,7 +126,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = GetVirtualizingLayoutContext(context); + var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); return virtualizingLayout.MeasureOverride(virtualizingContext, availableSize); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -157,7 +157,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = GetVirtualizingLayoutContext(context); + var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); return virtualizingLayout.ArrangeOverride(virtualizingContext, finalSize); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -184,7 +184,7 @@ namespace Avalonia.Layout /// protected void InvalidateArrange() => ArrangeInvalidated?.Invoke(this, EventArgs.Empty); - private VirtualizingLayoutContext GetVirtualizingLayoutContext(LayoutContext context) + private static VirtualizingLayoutContext GetVirtualizingLayoutContext(LayoutContext context) { if (context is VirtualizingLayoutContext virtualizingContext) { diff --git a/src/Avalonia.Base/Layout/StackLayout.cs b/src/Avalonia.Base/Layout/StackLayout.cs index e3c2ab3817..7983b37843 100644 --- a/src/Avalonia.Base/Layout/StackLayout.cs +++ b/src/Avalonia.Base/Layout/StackLayout.cs @@ -90,7 +90,7 @@ namespace Avalonia.Layout // Constants int itemsCount = context.ItemCount; var stackState = (StackLayoutState)context.LayoutState!; - double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing; + double averageElementSize = StackLayout.GetAverageElementSize(availableSize, context, stackState) + Spacing; _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds); _orientation.SetMajorSize(ref extent, Math.Max(0.0f, itemsCount * averageElementSize - Spacing)); @@ -178,7 +178,7 @@ namespace Avalonia.Layout { index = targetIndex; var state = (StackLayoutState)context.LayoutState!; - double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; + double averageElementSize = StackLayout.GetAverageElementSize(availableSize, context, state) + Spacing; offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent); } @@ -237,7 +237,7 @@ namespace Avalonia.Layout var state = (StackLayoutState)context.LayoutState!; var lastExtent = state.FlowAlgorithm.LastExtent; - double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; + double averageElementSize = StackLayout.GetAverageElementSize(availableSize, context, state) + Spacing; double realizationWindowOffsetInExtent = _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent); double majorSize = _orientation.MajorSize(lastExtent) == 0 ? Math.Max(0.0, averageElementSize * itemsCount - Spacing) : _orientation.MajorSize(lastExtent); if (itemsCount > 0 && @@ -335,7 +335,7 @@ namespace Avalonia.Layout InvalidateLayout(); } - private double GetAverageElementSize( + private static double GetAverageElementSize( Size availableSize, VirtualizingLayoutContext context, StackLayoutState stackLayoutState) diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs index a7880a1545..acb333bcfa 100644 --- a/src/Avalonia.Base/Layout/UniformGridLayout.cs +++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs @@ -432,7 +432,7 @@ namespace Avalonia.Layout var gridState = (UniformGridLayoutState)context.LayoutState!; gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns); - var desiredSize = GetFlowAlgorithm(context).Measure( + var desiredSize = UniformGridLayout.GetFlowAlgorithm(context).Measure( availableSize, context, true, @@ -452,7 +452,7 @@ namespace Avalonia.Layout protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) { - var value = GetFlowAlgorithm(context).Arrange( + var value = UniformGridLayout.GetFlowAlgorithm(context).Arrange( finalSize, context, true, @@ -463,7 +463,7 @@ namespace Avalonia.Layout protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object? source, NotifyCollectionChangedEventArgs args) { - GetFlowAlgorithm(context).OnItemsSourceChanged(source, args, context); + UniformGridLayout.GetFlowAlgorithm(context).OnItemsSourceChanged(source, args, context); // Always invalidate layout to keep the view accurate. InvalidateLayout(); @@ -557,6 +557,6 @@ namespace Avalonia.Layout private void InvalidateLayout() => InvalidateMeasure(); - private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm; + private static FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((UniformGridLayoutState)context.LayoutState!).FlowAlgorithm; } } diff --git a/src/Avalonia.Base/Platform/AssetLoader.cs b/src/Avalonia.Base/Platform/AssetLoader.cs index a74da2a178..77ae9f4c32 100644 --- a/src/Avalonia.Base/Platform/AssetLoader.cs +++ b/src/Avalonia.Base/Platform/AssetLoader.cs @@ -126,7 +126,7 @@ namespace Avalonia.Platform uri = uri.EnsureAbsolute(baseUri); if (uri.IsAvares()) { - var (asm, path) = GetResAsmAndPath(uri); + var (asm, path) = AssetLoader.GetResAsmAndPath(uri); if (asm == null) { throw new ArgumentException( @@ -171,7 +171,7 @@ namespace Avalonia.Platform if (uri.IsAvares()) { - var (asm, path) = GetResAsmAndPath(uri); + var (asm, path) = AssetLoader.GetResAsmAndPath(uri); if (asm.AvaloniaResources == null) return null; asm.AvaloniaResources.TryGetValue(path, out var desc); @@ -181,7 +181,7 @@ namespace Avalonia.Platform throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri)); } - private (IAssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) + private static (IAssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) { var asm = s_assemblyDescriptorResolver.GetAssembly(uri.Authority); return (asm, uri.GetUnescapeAbsolutePath()); @@ -194,7 +194,7 @@ namespace Avalonia.Platform if (!uri.IsAbsoluteUri) return null; if (uri.IsAvares()) - return GetResAsmAndPath(uri).asm; + return AssetLoader.GetResAsmAndPath(uri).asm; if (uri.IsResm()) { diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs index e20a4a9ad8..2b395f54af 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs @@ -87,7 +87,7 @@ namespace Avalonia.Rendering.Composition.Animations if (elapsed < _delayTime) { if (_delayBehavior == AnimationDelayBehavior.SetInitialValueBeforeDelay) - return ExpressionVariant.Create(GetKeyFrame(ref ctx, _keyFrames[0])); + return ExpressionVariant.Create(KeyFrameAnimationInstance.GetKeyFrame(ref ctx, _keyFrames[0])); return currentValue; } @@ -95,7 +95,7 @@ namespace Avalonia.Rendering.Composition.Animations var iterationNumber = elapsed.Ticks / _duration.Ticks; if (_iterationBehavior == AnimationIterationBehavior.Count && iterationNumber >= _iterationCount) - return ExpressionVariant.Create(GetKeyFrame(ref ctx, _keyFrames[_keyFrames.Length - 1])); + return ExpressionVariant.Create(KeyFrameAnimationInstance.GetKeyFrame(ref ctx, _keyFrames[_keyFrames.Length - 1])); var evenIterationNumber = iterationNumber % 2 == 0; @@ -124,7 +124,7 @@ namespace Avalonia.Rendering.Composition.Animations { // this is the last frame if (c == _keyFrames.Length - 1) - return ExpressionVariant.Create(GetKeyFrame(ref ctx, kf)); + return ExpressionVariant.Create(KeyFrameAnimationInstance.GetKeyFrame(ref ctx, kf)); left = kf; right = _keyFrames[c + 1]; @@ -139,13 +139,13 @@ namespace Avalonia.Rendering.Composition.Animations return currentValue; return ExpressionVariant.Create(_interpolator.Interpolate( - GetKeyFrame(ref ctx, left), - GetKeyFrame(ref ctx, right), + KeyFrameAnimationInstance.GetKeyFrame(ref ctx, left), + KeyFrameAnimationInstance.GetKeyFrame(ref ctx, right), easedKeyProgress )); } - T GetKeyFrame(ref ExpressionEvaluationContext ctx, ServerKeyFrame f) + static T GetKeyFrame(ref ExpressionEvaluationContext ctx, ServerKeyFrame f) { if (f.Expression != null) return f.Expression.Evaluate(ref ctx).CastOrDefault(); diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 98a6a3600e..3c9a9feac0 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -124,7 +124,7 @@ public class CompositingRenderer : IRendererWithCompositor QueueUpdate(); } - private void SyncChildren(Visual v) + private static void SyncChildren(Visual v) { //TODO: Optimize by moving that logic to Visual itself if(v.CompositionVisual == null) @@ -233,11 +233,11 @@ public class CompositingRenderer : IRendererWithCompositor visual.Render(_recordingContext); comp.DrawList = _recorder.EndUpdate(); - SyncChildren(visual); + CompositingRenderer.SyncChildren(visual); } foreach(var v in _recalculateChildren) if (!_dirty.Contains(v)) - SyncChildren(v); + CompositingRenderer.SyncChildren(v); _dirty.Clear(); _recalculateChildren.Clear(); CompositionTarget.Size = _root.ClientSize; diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs index d8a608651b..52215e8011 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs @@ -53,7 +53,7 @@ namespace Avalonia.Rendering.Composition var m = Matrix.Identity; while (v != null) { - if (!TryGetInvertedTransform(v, out var cm)) + if (!CompositionTarget.TryGetInvertedTransform(v, out var cm)) return null; m = m * cm; v = v.Parent; @@ -62,7 +62,7 @@ namespace Avalonia.Rendering.Composition return point * m; } - bool TryGetInvertedTransform(CompositionVisual visual, out Matrix matrix) + static bool TryGetInvertedTransform(CompositionVisual visual, out Matrix matrix) { var m = visual.TryGetServerGlobalTransform(); if (m == null) @@ -78,7 +78,7 @@ namespace Avalonia.Rendering.Composition bool TryTransformTo(CompositionVisual visual, Point globalPoint, out Point v) { v = default; - if (TryGetInvertedTransform(visual, out var m)) + if (CompositionTarget.TryGetInvertedTransform(visual, out var m)) { v = globalPoint * m; return true; diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs index 30b57883fc..c02eb5687f 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs @@ -55,7 +55,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW if (next == null || !next.Item.Equals(Transform, brush, pen, geometry)) { - Add(new GeometryNode(Transform, brush, pen, geometry, CreateChildScene(brush))); + Add(new GeometryNode(Transform, brush, pen, geometry, CompositionDrawingContext.CreateChildScene(brush))); } else { @@ -94,7 +94,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW if (next == null || !next.Item.Equals(Transform, pen, p1, p2)) { - Add(new LineNode(Transform, pen, p1, p2, CreateChildScene(pen.Brush))); + Add(new LineNode(Transform, pen, p1, p2, CompositionDrawingContext.CreateChildScene(pen.Brush))); } else { @@ -110,7 +110,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW if (next == null || !next.Item.Equals(Transform, brush, pen, rect, boxShadows)) { - Add(new RectangleNode(Transform, brush, pen, rect, boxShadows, CreateChildScene(brush))); + Add(new RectangleNode(Transform, brush, pen, rect, boxShadows, CompositionDrawingContext.CreateChildScene(brush))); } else { @@ -139,7 +139,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW if (next == null || !next.Item.Equals(Transform, brush, pen, rect)) { - Add(new EllipseNode(Transform, brush, pen, rect, CreateChildScene(brush))); + Add(new EllipseNode(Transform, brush, pen, rect, CompositionDrawingContext.CreateChildScene(brush))); } else { @@ -165,7 +165,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW if (next == null || !next.Item.Equals(Transform, foreground, glyphRun)) { - Add(new GlyphRunNode(Transform, foreground, glyphRun, CreateChildScene(foreground))); + Add(new GlyphRunNode(Transform, foreground, glyphRun, CompositionDrawingContext.CreateChildScene(foreground))); } else @@ -324,7 +324,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW if (next == null || !next.Item.Equals(mask, bounds)) { - Add(new OpacityMaskNode(mask, bounds, CreateChildScene(mask))); + Add(new OpacityMaskNode(mask, bounds, CompositionDrawingContext.CreateChildScene(mask))); } else { @@ -368,7 +368,7 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW : null; } - private IDisposable? CreateChildScene(IBrush? brush) + private static IDisposable? CreateChildScene(IBrush? brush) { if (brush is VisualBrush visualBrush) { diff --git a/src/Avalonia.Base/Rendering/DeferredRenderer.cs b/src/Avalonia.Base/Rendering/DeferredRenderer.cs index 4236763e3b..1eefa5a177 100644 --- a/src/Avalonia.Base/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Base/Rendering/DeferredRenderer.cs @@ -272,18 +272,18 @@ namespace Avalonia.Rendering } } - Scene? TryGetChildScene(IRef? op) => (op?.Item as BrushDrawOperation)?.Aux as Scene; + static Scene? TryGetChildScene(IRef? op) => (op?.Item as BrushDrawOperation)?.Aux as Scene; /// Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush) { - return TryGetChildScene(_currentDraw)?.Size ?? Size.Empty; + return DeferredRenderer.TryGetChildScene(_currentDraw)?.Size ?? Size.Empty; } /// void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush) { - var childScene = TryGetChildScene(_currentDraw); + var childScene = DeferredRenderer.TryGetChildScene(_currentDraw); if (childScene != null) { diff --git a/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs index 0ceb44ed75..a34cce8796 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs @@ -18,7 +18,7 @@ namespace Avalonia.Rendering.SceneGraph _ = scene ?? throw new ArgumentNullException(nameof(scene)); Dispatcher.UIThread.VerifyAccess(); - UpdateSize(scene); + SceneBuilder.UpdateSize(scene); scene.Layers.GetOrAdd(scene.Root.Visual); using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) @@ -46,7 +46,7 @@ namespace Avalonia.Rendering.SceneGraph if (visual == scene.Root.Visual) { - UpdateSize(scene); + SceneBuilder.UpdateSize(scene); } if (visual.VisualRoot == scene.Root.Visual) @@ -318,7 +318,7 @@ namespace Avalonia.Rendering.SceneGraph } } - private void UpdateSize(Scene scene) + private static void UpdateSize(Scene scene) { var renderRoot = scene.Root.Visual as IRenderRoot; var newSize = renderRoot?.ClientSize ?? scene.Root.Visual.Bounds.Size; From ee84a8c7fd242a76353aad24d83d483d344f6b83 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:08:25 +0200 Subject: [PATCH 005/173] feat(Controls): Address rule CA1822 --- .../Automation/Peers/ComboBoxAutomationPeer.cs | 8 ++++---- .../Calendar/CalendarBlackoutDatesCollection.cs | 10 +++++----- .../Calendar/SelectedDatesCollection.cs | 10 +++++----- src/Avalonia.Controls/ComboBox.cs | 8 ++++---- .../DateTimePickers/DateTimePickerPanel.cs | 6 +++--- src/Avalonia.Controls/Grid.cs | 6 +++--- src/Avalonia.Controls/GridSplitter.cs | 16 ++++++++-------- .../Platform/InProcessDragSource.cs | 4 ++-- .../Presenters/ScrollContentPresenter.cs | 8 ++++---- src/Avalonia.Controls/Primitives/AdornerLayer.cs | 4 ++-- src/Avalonia.Controls/Primitives/Popup.cs | 4 ++-- src/Avalonia.Controls/Repeater/RecyclePool.cs | 10 +++++----- .../Repeater/RecyclingElementFactory.cs | 4 ++-- src/Avalonia.Controls/SplitView.cs | 6 +++--- src/Avalonia.Controls/TextBox.cs | 10 +++++----- src/Avalonia.Controls/TopLevel.cs | 4 ++-- src/Avalonia.Controls/TreeView.cs | 6 +++--- src/Avalonia.Controls/UserControl.cs | 1 - 18 files changed, 62 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs index 5ff291d972..d6295fdbd9 100644 --- a/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs @@ -18,7 +18,7 @@ namespace Avalonia.Automation.Peers public new ComboBox Owner => (ComboBox)base.Owner; - public ExpandCollapseState ExpandCollapseState => ToState(Owner.IsDropDownOpen); + public ExpandCollapseState ExpandCollapseState => ComboBoxAutomationPeer.ToState(Owner.IsDropDownOpen); public bool ShowsMenu => true; public void Collapse() => Owner.IsDropDownOpen = false; public void Expand() => Owner.IsDropDownOpen = true; @@ -66,12 +66,12 @@ namespace Avalonia.Automation.Peers { RaisePropertyChangedEvent( ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, - ToState((bool)e.OldValue!), - ToState((bool)e.NewValue!)); + ComboBoxAutomationPeer.ToState((bool)e.OldValue!), + ComboBoxAutomationPeer.ToState((bool)e.NewValue!)); } } - private ExpandCollapseState ToState(bool value) + private static ExpandCollapseState ToState(bool value) { return value ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; } diff --git a/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs b/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs index a92feec509..61463795ee 100644 --- a/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs @@ -122,7 +122,7 @@ namespace Avalonia.Controls.Primitives /// protected override void ClearItems() { - EnsureValidThread(); + CalendarBlackoutDatesCollection.EnsureValidThread(); base.ClearItems(); _owner.UpdateMonths(); @@ -140,7 +140,7 @@ namespace Avalonia.Controls.Primitives /// protected override void InsertItem(int index, CalendarDateRange item) { - EnsureValidThread(); + CalendarBlackoutDatesCollection.EnsureValidThread(); if (!IsValid(item)) { @@ -162,7 +162,7 @@ namespace Avalonia.Controls.Primitives /// protected override void RemoveItem(int index) { - EnsureValidThread(); + CalendarBlackoutDatesCollection.EnsureValidThread(); base.RemoveItem(index); _owner.UpdateMonths(); @@ -182,7 +182,7 @@ namespace Avalonia.Controls.Primitives /// protected override void SetItem(int index, CalendarDateRange item) { - EnsureValidThread(); + CalendarBlackoutDatesCollection.EnsureValidThread(); if (!IsValid(item)) { @@ -206,7 +206,7 @@ namespace Avalonia.Controls.Primitives return true; } - private void EnsureValidThread() + private static void EnsureValidThread() { Dispatcher.UIThread.VerifyAccess(); } diff --git a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs index 211b5edb0d..8327442fcf 100644 --- a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs @@ -133,7 +133,7 @@ namespace Avalonia.Controls.Primitives /// protected override void ClearItems() { - EnsureValidThread(); + SelectedDatesCollection.EnsureValidThread(); Collection addedItems = new Collection(); Collection removedItems = new Collection(); @@ -170,7 +170,7 @@ namespace Avalonia.Controls.Primitives /// protected override void InsertItem(int index, DateTime item) { - EnsureValidThread(); + SelectedDatesCollection.EnsureValidThread(); if (!Contains(item)) { @@ -233,7 +233,7 @@ namespace Avalonia.Controls.Primitives /// protected override void RemoveItem(int index) { - EnsureValidThread(); + SelectedDatesCollection.EnsureValidThread(); if (index >= Count) { @@ -284,7 +284,7 @@ namespace Avalonia.Controls.Primitives /// protected override void SetItem(int index, DateTime item) { - EnsureValidThread(); + SelectedDatesCollection.EnsureValidThread(); if (!Contains(item)) { @@ -353,7 +353,7 @@ namespace Avalonia.Controls.Primitives return true; } - private void EnsureValidThread() + private static void EnsureValidThread() { Dispatcher.UIThread.VerifyAccess(); } diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 54196bdf1a..21918b27a8 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -236,7 +236,7 @@ namespace Avalonia.Controls else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 && (e.Key == Key.Up || e.Key == Key.Down) && IsFocused == true) { - var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c)); + var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => ComboBox.CanFocus(c)); if (firstChild != null) { FocusManager.Instance?.Focus(firstChild, NavigationMethod.Directional); @@ -341,7 +341,7 @@ namespace Avalonia.Controls { _subscriptionsOnOpen.Clear(); - if (CanFocus(this)) + if (ComboBox.CanFocus(this)) { Focus(); } @@ -403,14 +403,14 @@ namespace Avalonia.Controls container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); } - if (container != null && CanFocus(container)) + if (container != null && ComboBox.CanFocus(container)) { container.Focus(); } } } - private bool CanFocus(IControl control) => control.Focusable && control.IsEffectivelyEnabled && control.IsVisible; + private static bool CanFocus(IControl control) => control.Focusable && control.IsEffectivelyEnabled && control.IsVisible; private void UpdateSelectionBoxItem(object? item) { diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index 3fdfbee54d..eb4a7391b0 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -545,8 +545,8 @@ namespace Avalonia.Controls.Primitives private void OnItemTapped(object? sender, TappedEventArgs e) { - if (e.Source is IVisual source && - GetItemFromSource(source) is ListBoxItem listBoxItem && + if (e.Source is IVisual source && + DateTimePickerPanel.GetItemFromSource(source) is ListBoxItem listBoxItem && listBoxItem.Tag is int tag) { SelectedValue = tag; @@ -555,7 +555,7 @@ namespace Avalonia.Controls.Primitives } //Helper to get ListBoxItem from pointerevent source - private ListBoxItem? GetItemFromSource(IVisual src) + private static ListBoxItem? GetItemFromSource(IVisual src) { var item = src; while (item != null && !(item is ListBoxItem)) diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 8d246d35f4..4dc88fec6a 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -1117,7 +1117,7 @@ namespace Avalonia.Controls else { // otherwise... - cellMeasureWidth = GetMeasureSizeForRange( + cellMeasureWidth = Grid.GetMeasureSizeForRange( DefinitionsU, PrivateCells[cell].ColumnIndex, PrivateCells[cell].ColumnSpan); @@ -1137,7 +1137,7 @@ namespace Avalonia.Controls } else { - cellMeasureHeight = GetMeasureSizeForRange( + cellMeasureHeight = Grid.GetMeasureSizeForRange( DefinitionsV, PrivateCells[cell].RowIndex, PrivateCells[cell].RowSpan); @@ -1165,7 +1165,7 @@ namespace Avalonia.Controls /// /// For "Auto" definitions MinWidth is used in place of PreferredSize. /// - private double GetMeasureSizeForRange( + private static double GetMeasureSizeForRange( IReadOnlyList definitions, int start, int count) diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index 85dad894fd..1a4736fa92 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -288,13 +288,13 @@ namespace Avalonia.Controls _resizeData.Definition1 = GetGridDefinition(_resizeData.Grid, index1, _resizeData.ResizeDirection); _resizeData.OriginalDefinition1Length = _resizeData.Definition1.UserSizeValueCache; // Save Size if user cancels. - _resizeData.OriginalDefinition1ActualLength = GetActualLength(_resizeData.Definition1); + _resizeData.OriginalDefinition1ActualLength = GridSplitter.GetActualLength(_resizeData.Definition1); _resizeData.Definition2Index = index2; _resizeData.Definition2 = GetGridDefinition(_resizeData.Grid, index2, _resizeData.ResizeDirection); _resizeData.OriginalDefinition2Length = _resizeData.Definition2.UserSizeValueCache; // Save Size if user cancels. - _resizeData.OriginalDefinition2ActualLength = GetActualLength(_resizeData.Definition2); + _resizeData.OriginalDefinition2ActualLength = GridSplitter.GetActualLength(_resizeData.Definition2); // Determine how to resize the columns. bool isStar1 = IsStar(_resizeData.Definition1); @@ -516,7 +516,7 @@ namespace Avalonia.Controls /// /// Retrieves the ActualWidth or ActualHeight of the definition depending on its type Column or Row. /// - private double GetActualLength(DefinitionBase definition) + private static double GetActualLength(DefinitionBase definition) { var column = definition as ColumnDefinition; @@ -537,11 +537,11 @@ namespace Avalonia.Controls /// private void GetDeltaConstraints(out double minDelta, out double maxDelta) { - double definition1Len = GetActualLength(_resizeData!.Definition1!); + double definition1Len = GridSplitter.GetActualLength(_resizeData!.Definition1!); double definition1Min = _resizeData.Definition1!.UserMinSizeValueCache; double definition1Max = _resizeData.Definition1.UserMaxSizeValueCache; - double definition2Len = GetActualLength(_resizeData.Definition2!); + double definition2Len = GridSplitter.GetActualLength(_resizeData.Definition2!); double definition2Min = _resizeData.Definition2!.UserMinSizeValueCache; double definition2Max = _resizeData.Definition2.UserMaxSizeValueCache; @@ -590,7 +590,7 @@ namespace Avalonia.Controls } else if (IsStar(definition)) { - SetDefinitionLength(definition, new GridLength(GetActualLength(definition), GridUnitType.Star)); + SetDefinitionLength(definition, new GridLength(GridSplitter.GetActualLength(definition), GridUnitType.Star)); } } } @@ -629,8 +629,8 @@ namespace Avalonia.Controls if (definition1 != null && definition2 != null) { - double actualLength1 = GetActualLength(definition1); - double actualLength2 = GetActualLength(definition2); + double actualLength1 = GridSplitter.GetActualLength(definition1); + double actualLength2 = GridSplitter.GetActualLength(definition2); double pixelLength = 1 / _resizeData.Scaling; double epsilon = pixelLength + LayoutHelper.LayoutEpsilon; diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs index 209d5f03dc..e107d2c217 100644 --- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -64,12 +64,12 @@ namespace Avalonia.Platform var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); tl?.PlatformImpl?.Input?.Invoke(rawEvent); - var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); + var effect = InProcessDragSource.GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); UpdateCursor(root, effect); return effect; } - private DragDropEffects GetPreferredEffect(DragDropEffects effect, RawInputModifiers modifiers) + private static DragDropEffects GetPreferredEffect(DragDropEffects effect, RawInputModifiers modifiers) { if (effect == DragDropEffects.Copy || effect == DragDropEffects.Move || effect == DragDropEffects.Link || effect == DragDropEffects.None) return effect; // No need to check for the modifiers. diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index c526b7ac49..e5bf924120 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -295,7 +295,7 @@ namespace Avalonia.Controls.Presenters // arrange then that change wasn't just due to scrolling (as scrolling doesn't adjust // relative positions within Child). if (_anchorElement != null && - TranslateBounds(_anchorElement, Child!, out var updatedBounds) && + ScrollContentPresenter.TranslateBounds(_anchorElement, Child!, out var updatedBounds) && updatedBounds.Position != _anchorElementBounds.Position) { var offset = updatedBounds.Position - _anchorElementBounds.Position; @@ -588,7 +588,7 @@ namespace Avalonia.Controls.Presenters private bool GetViewportBounds(IControl element, out Rect bounds) { - if (TranslateBounds(element, Child!, out var childBounds)) + if (ScrollContentPresenter.TranslateBounds(element, Child!, out var childBounds)) { // We want the bounds relative to the new Offset, regardless of whether the child // control has actually been arranged to this offset yet, so translate first to the @@ -605,7 +605,7 @@ namespace Avalonia.Controls.Presenters private Rect TranslateBounds(IControl control, IControl to) { - if (TranslateBounds(control, to, out var bounds)) + if (ScrollContentPresenter.TranslateBounds(control, to, out var bounds)) { return bounds; } @@ -613,7 +613,7 @@ namespace Avalonia.Controls.Presenters throw new InvalidOperationException("The control's bounds could not be translated to the requested control."); } - private bool TranslateBounds(IControl control, IControl to, out Rect bounds) + private static bool TranslateBounds(IControl control, IControl to, out Rect bounds) { if (!control.IsVisible) { diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index d557424fbb..12a17b8c9f 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -211,7 +211,7 @@ namespace Avalonia.Controls.Primitives { child.RenderTransform = new MatrixTransform(info.Bounds.Value.Transform); child.RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Absolute); - UpdateClip(child, info.Bounds.Value, isClipEnabled); + AdornerLayer.UpdateClip(child, info.Bounds.Value, isClipEnabled); child.Arrange(info.Bounds.Value.Bounds); } else @@ -232,7 +232,7 @@ namespace Avalonia.Controls.Primitives layer?.UpdateAdornedElement(adorner, adorned); } - private void UpdateClip(IControl control, TransformedBounds bounds, bool isEnabled) + private static void UpdateClip(IControl control, TransformedBounds bounds, bool isEnabled) { if (!isEnabled) { diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index ccb81ba276..581a65f6b9 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -475,7 +475,7 @@ namespace Avalonia.Controls.Primitives _openState = new PopupOpenState(placementTarget, topLevel, popupHost, cleanupPopup); - WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); + Popup.WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); popupHost.Show(); @@ -639,7 +639,7 @@ namespace Avalonia.Controls.Primitives return Disposable.Create((unsubscribe, target, handler), state => state.unsubscribe(state.target, state.handler)); } - private void WindowManagerAddShadowHintChanged(IPopupHost host, bool hint) + private static void WindowManagerAddShadowHintChanged(IPopupHost host, bool hint) { if(host is PopupRoot pr && pr.PlatformImpl is not null) { diff --git a/src/Avalonia.Controls/Repeater/RecyclePool.cs b/src/Avalonia.Controls/Repeater/RecyclePool.cs index cf2b40836e..9d6ad37721 100644 --- a/src/Avalonia.Controls/Repeater/RecyclePool.cs +++ b/src/Avalonia.Controls/Repeater/RecyclePool.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls public void PutElement(IControl element, string key, IControl? owner) { - var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner); + var ownerAsPanel = RecyclePool.EnsureOwnerIsPanelOrNull(owner); var elementInfo = new ElementInfo(element, ownerAsPanel); if (!_elements.TryGetValue(key, out var pool)) @@ -56,7 +56,7 @@ namespace Avalonia.Controls var elementInfo = elements.FirstOrDefault(x => x.Owner == owner) ?? elements.LastOrDefault(); elements.Remove(elementInfo!); - var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner); + var ownerAsPanel = RecyclePool.EnsureOwnerIsPanelOrNull(owner); if (elementInfo!.Owner != null && elementInfo.Owner != ownerAsPanel) { // Element is still under its parent. remove it from its parent. @@ -80,10 +80,10 @@ namespace Avalonia.Controls return null; } - internal string GetReuseKey(IControl element) => ((Control)element).GetValue(ReuseKeyProperty); - internal void SetReuseKey(IControl element, string value) => ((Control)element).SetValue(ReuseKeyProperty, value); + internal static string GetReuseKey(IControl element) => ((Control)element).GetValue(ReuseKeyProperty); + internal static void SetReuseKey(IControl element, string value) => ((Control)element).SetValue(ReuseKeyProperty, value); - private IPanel? EnsureOwnerIsPanelOrNull(IControl? owner) + private static IPanel? EnsureOwnerIsPanelOrNull(IControl? owner) { if (owner is IPanel panel) { diff --git a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs index c1baa66433..1258c58324 100644 --- a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs +++ b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs @@ -72,7 +72,7 @@ namespace Avalonia.Controls element = dataTemplate.Build(args.Data)!; // Associate ReuseKey with element - RecyclePool.SetReuseKey(element, templateKey); + Avalonia.Controls.RecyclePool.SetReuseKey(element, templateKey); } return element; @@ -81,7 +81,7 @@ namespace Avalonia.Controls protected override void RecycleElementCore(ElementFactoryRecycleArgs args) { var element = args.Element!; - var key = RecyclePool.GetReuseKey(element); + var key = Avalonia.Controls.RecyclePool.GetReuseKey(element); RecyclePool.PutElement(element, key, args.Parent); } diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs index c344dd795d..2d735a2cbb 100644 --- a/src/Avalonia.Controls/SplitView.cs +++ b/src/Avalonia.Controls/SplitView.cs @@ -431,7 +431,7 @@ namespace Avalonia.Controls } } - private string GetPseudoClass(SplitViewDisplayMode mode) + private static string GetPseudoClass(SplitViewDisplayMode mode) { return mode switch { @@ -463,8 +463,8 @@ namespace Avalonia.Controls private void OnDisplayModeChanged(AvaloniaPropertyChangedEventArgs e) { - var oldState = GetPseudoClass(e.GetOldValue()); - var newState = GetPseudoClass(e.GetNewValue()); + var oldState = SplitView.GetPseudoClass(e.GetOldValue()); + var newState = SplitView.GetPseudoClass(e.GetNewValue()); PseudoClasses.Remove($":{oldState}"); PseudoClasses.Add($":{newState}"); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index da4e90fb66..0ac57c9233 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -397,9 +397,9 @@ namespace Avalonia.Controls var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; - CaretIndex = CoerceCaretIndex(caretIndex, value); - SelectionStart = CoerceCaretIndex(selectionStart, value); - SelectionEnd = CoerceCaretIndex(selectionEnd, value); + CaretIndex = TextBox.CoerceCaretIndex(caretIndex, value); + SelectionStart = TextBox.CoerceCaretIndex(selectionStart, value); + SelectionEnd = TextBox.CoerceCaretIndex(selectionEnd, value); var textChanged = SetAndRaise(TextProperty, ref _text, value); @@ -1380,9 +1380,9 @@ namespace Avalonia.Controls } } - private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text); + private int CoerceCaretIndex(int value) => TextBox.CoerceCaretIndex(value, Text); - private int CoerceCaretIndex(int value, string? text) + private static int CoerceCaretIndex(int value, string? text) { if (text == null) { diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 47fc9d7988..9fad9824df 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -429,7 +429,7 @@ namespace Avalonia.Controls LayoutHelper.InvalidateSelfAndChildrenMeasure(this); } - private bool TransparencyLevelsMatch (WindowTransparencyLevel requested, WindowTransparencyLevel received) + private static bool TransparencyLevelsMatch (WindowTransparencyLevel requested, WindowTransparencyLevel received) { if(requested == received) { @@ -449,7 +449,7 @@ namespace Avalonia.Controls { if(transparencyLevel == WindowTransparencyLevel.None || TransparencyLevelHint == WindowTransparencyLevel.None || - !TransparencyLevelsMatch(TransparencyLevelHint, transparencyLevel)) + !TopLevel.TransparencyLevelsMatch(TransparencyLevelHint, transparencyLevel)) { _transparencyFallbackBorder.Background = TransparencyBackgroundFallback; } diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index d78f9c82ef..be30792cc8 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -284,7 +284,7 @@ namespace Avalonia.Controls foreach (IControl container in ItemContainerGenerator.Index!.Containers) { - MarkContainerSelected(container, false); + TreeView.MarkContainerSelected(container, false); } if (SelectedItems.Count > 0) @@ -339,7 +339,7 @@ namespace Avalonia.Controls { var container = ItemContainerGenerator.Index!.ContainerFromItem(item)!; - MarkContainerSelected(container, selected); + TreeView.MarkContainerSelected(container, selected); } private void SelectedItemsAdded(IList items) @@ -826,7 +826,7 @@ namespace Avalonia.Controls /// /// The container. /// Whether the control is selected - private void MarkContainerSelected(IControl container, bool selected) + private static void MarkContainerSelected(IControl container, bool selected) { if (container == null) { diff --git a/src/Avalonia.Controls/UserControl.cs b/src/Avalonia.Controls/UserControl.cs index 7b9cc2da1c..e9339d5f4b 100644 --- a/src/Avalonia.Controls/UserControl.cs +++ b/src/Avalonia.Controls/UserControl.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Styling; namespace Avalonia.Controls From 4620e5cf99ff0a82832d117b23ca97ffb66c764d Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:09:16 +0200 Subject: [PATCH 006/173] feat(ColorPicker): Address rule CA1822 --- .../ColorPalettes/MaterialColorPalette.cs | 8 ++++---- .../ColorSlider/ColorSlider.cs | 6 +++--- .../ColorSpectrum/ColorSpectrum.cs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs index 5cf5662ede..555965a9a7 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs @@ -35,7 +35,7 @@ namespace Avalonia.Controls /// This is pulled out separately to lazy load for performance. /// If no material color palette is ever used, no colors will be created. /// - private void InitColorChart() + private static void InitColorChart() { lock (_colorChartMutex) { @@ -322,7 +322,7 @@ namespace Avalonia.Controls { if (_colorChart == null) { - InitColorChart(); + MaterialColorPalette.InitColorChart(); } return _colorChartColorCount; @@ -336,7 +336,7 @@ namespace Avalonia.Controls { if (_colorChart == null) { - InitColorChart(); + MaterialColorPalette.InitColorChart(); } return _colorChartShadeCount; @@ -348,7 +348,7 @@ namespace Avalonia.Controls { if (_colorChart == null) { - InitColorChart(); + MaterialColorPalette.InitColorChart(); } return _colorChart![ diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index b662d20223..fab2b35969 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -121,7 +121,7 @@ namespace Avalonia.Controls.Primitives /// /// The to round component values for. /// A new with rounded component values. - private HsvColor RoundComponentValues(HsvColor hsvColor) + private static HsvColor RoundComponentValues(HsvColor hsvColor) { return new HsvColor( Math.Round(hsvColor.A, 2, MidpointRounding.AwayFromZero), @@ -147,7 +147,7 @@ namespace Avalonia.Controls.Primitives if (IsRoundingEnabled) { - hsvColor = RoundComponentValues(hsvColor); + hsvColor = ColorSlider.RoundComponentValues(hsvColor); } // Note: Components converted into a usable range for the user @@ -272,7 +272,7 @@ namespace Avalonia.Controls.Primitives if (IsRoundingEnabled) { - hsvColor = RoundComponentValues(hsvColor); + hsvColor = ColorSlider.RoundComponentValues(hsvColor); } return (rgbColor, hsvColor); diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index bd44161a42..1ff1445d8e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -1027,7 +1027,7 @@ namespace Avalonia.Controls.Primitives { for (int y = pixelDimension - 1; y >= 0; --y) { - FillPixelForBox( + ColorSpectrum.FillPixelForBox( x, y, hsv, pixelDimension, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); @@ -1099,7 +1099,7 @@ namespace Avalonia.Controls.Primitives }); } - private void FillPixelForBox( + private static void FillPixelForBox( double x, double y, Hsv baseHsv, From b8b180f64c781d782e7f9a044210a5a0a2192fd9 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:09:38 +0200 Subject: [PATCH 007/173] feat(DataGrid): Address rule CA1822 --- src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs index 0e754d5815..023ae99266 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs @@ -56,7 +56,7 @@ namespace Avalonia.Controls set => SetAndRaise(CellEditingTemplateProperty, ref _cellEditingCellTemplate, value); } - private void OnCellTemplateChanged(AvaloniaPropertyChangedEventArgs e) + private static void OnCellTemplateChanged(AvaloniaPropertyChangedEventArgs e) { var oldValue = (IDataTemplate)e.OldValue; var value = (IDataTemplate)e.NewValue; From bf1f6f04399b2b4290e025d714741a435ea04b25 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:09:57 +0200 Subject: [PATCH 008/173] feat(Diagnostic): Address rule CA1822 --- .../Diagnostics/Screenshots/FilePickerHandler.cs | 4 ++-- .../Diagnostics/ViewModels/ControlDetailsViewModel.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs b/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs index 4153d2d38c..325c55783c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs @@ -47,7 +47,7 @@ namespace Avalonia.Diagnostics.Screenshots /// public string Title { get; } = "Save Screenshot to ..."; - Window GetWindow(IControl control) + static Window GetWindow(IControl control) { var window = control.VisualRoot as Window; var app = Application.Current; @@ -61,7 +61,7 @@ namespace Avalonia.Diagnostics.Screenshots protected async override Task GetStream(IControl control) { Stream? output = default; - var result = await GetWindow(control).StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + var result = await FilePickerHandler.GetWindow(control).StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { SuggestedStartLocation = new BclStorageFolder(new DirectoryInfo(ScreenshotsRoot)), Title = Title, diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 631da80d8b..869b2fd600 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -83,7 +83,7 @@ namespace Avalonia.Diagnostics.ViewModels { var setterValue = regularSetter.Value; - var resourceInfo = GetResourceInfo(setterValue); + var resourceInfo = ControlDetailsViewModel.GetResourceInfo(setterValue); SetterViewModel setterVm; @@ -122,7 +122,7 @@ namespace Avalonia.Diagnostics.ViewModels public bool CanNavigateToParentProperty => _selectedEntitiesStack.Count >= 1; - private (object resourceKey, bool isDynamic)? GetResourceInfo(object? value) + private static (object resourceKey, bool isDynamic)? GetResourceInfo(object? value) { if (value is StaticResourceExtension staticResource) { From e014c941447f4f2a2243a697cf62b4fa1e248202 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:10:13 +0200 Subject: [PATCH 009/173] feat(Dialogs): Address rule CA1822 --- src/Avalonia.Dialogs/ManagedStorageProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Dialogs/ManagedStorageProvider.cs b/src/Avalonia.Dialogs/ManagedStorageProvider.cs index 37d2781692..06c2c29dab 100644 --- a/src/Avalonia.Dialogs/ManagedStorageProvider.cs +++ b/src/Avalonia.Dialogs/ManagedStorageProvider.cs @@ -28,7 +28,7 @@ public class ManagedStorageProvider : BclStorageProvider where T : Window, ne public override async Task> OpenFilePickerAsync(FilePickerOpenOptions options) { var model = new ManagedFileChooserViewModel(options, _managedOptions); - var results = await Show(model, _parent); + var results = await ManagedStorageProvider.Show(model, _parent); return results.Select(f => new BclStorageFile(new FileInfo(f))).ToArray(); } @@ -36,7 +36,7 @@ public class ManagedStorageProvider : BclStorageProvider where T : Window, ne public override async Task SaveFilePickerAsync(FilePickerSaveOptions options) { var model = new ManagedFileChooserViewModel(options, _managedOptions); - var results = await Show(model, _parent); + var results = await ManagedStorageProvider.Show(model, _parent); return results.FirstOrDefault() is { } result ? new BclStorageFile(new FileInfo(result)) @@ -46,12 +46,12 @@ public class ManagedStorageProvider : BclStorageProvider where T : Window, ne public override async Task> OpenFolderPickerAsync(FolderPickerOpenOptions options) { var model = new ManagedFileChooserViewModel(options, _managedOptions); - var results = await Show(model, _parent); + var results = await ManagedStorageProvider.Show(model, _parent); return results.Select(f => new BclStorageFolder(new DirectoryInfo(f))).ToArray(); } - private async Task Show(ManagedFileChooserViewModel model, Window parent) + private static async Task Show(ManagedFileChooserViewModel model, Window parent) { var dialog = new T { From aa17cdf1dc5395e0184a2967450ba86e0f2ba7f5 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:10:42 +0200 Subject: [PATCH 010/173] feat(FreedDesktop): Address rule CA1822 --- src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs b/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs index 39ddd9d769..2d6a2569fb 100644 --- a/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs +++ b/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs @@ -34,7 +34,7 @@ namespace Avalonia.FreeDesktop Poll(0); } - private string GetSymlinkTarget(string x) => Path.GetFullPath(Path.Combine(DevByLabelDir, NativeMethods.ReadLink(x))); + private static string GetSymlinkTarget(string x) => Path.GetFullPath(Path.Combine(DevByLabelDir, NativeMethods.ReadLink(x))); private string UnescapeString(string input, string regexText, int escapeBase) => new Regex(regexText).Replace(input, m => Convert.ToChar(Convert.ToByte(m.Groups[1].Value, escapeBase)).ToString()); @@ -61,7 +61,7 @@ namespace Avalonia.FreeDesktop new DirectoryInfo(DevByLabelDir).GetFiles() : Enumerable.Empty(); var labelDevPathPairs = labelDirEnum - .Select(x => (GetSymlinkTarget(x.FullName), UnescapeDeviceLabel(x.Name))); + .Select(x => (LinuxMountedVolumeInfoListener.GetSymlinkTarget(x.FullName), UnescapeDeviceLabel(x.Name))); var q1 = from mount in fProcMounts join device in fProcPartitions on mount.Item1 equals device.Item2 From 3a9404171f4d8c8c4a0598403a9db04d776ba2a1 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:11:00 +0200 Subject: [PATCH 011/173] feat(Native): Address rule CA1822 --- src/Avalonia.Native/AvaloniaNativeDragSource.cs | 6 +++--- src/Avalonia.Native/AvaloniaNativeMenuExporter.cs | 4 ++-- src/Avalonia.Native/IAvnMenu.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Native/AvaloniaNativeDragSource.cs b/src/Avalonia.Native/AvaloniaNativeDragSource.cs index f91a299b3b..f93d558d25 100644 --- a/src/Avalonia.Native/AvaloniaNativeDragSource.cs +++ b/src/Avalonia.Native/AvaloniaNativeDragSource.cs @@ -19,8 +19,8 @@ namespace Avalonia.Native { _factory = factory; } - - TopLevel FindRoot(IInteractive interactive) + + static TopLevel FindRoot(IInteractive interactive) { while (interactive != null && !(interactive is IVisual)) interactive = interactive.InteractiveParent; @@ -48,7 +48,7 @@ namespace Avalonia.Native public Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects) { // Sanity check - var tl = FindRoot(triggerEvent.Source); + var tl = AvaloniaNativeDragSource.FindRoot(triggerEvent.Source); var view = tl?.PlatformImpl as WindowBaseImpl; if (view == null) throw new ArgumentException(); diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index d8753efe25..ddabfe8f5d 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -65,7 +65,7 @@ namespace Avalonia.Native } } - private NativeMenu CreateDefaultAppMenu() + private static NativeMenu CreateDefaultAppMenu() { var result = new NativeMenu(); @@ -167,7 +167,7 @@ namespace Avalonia.Native if (appMenu == null) { - appMenu = CreateDefaultAppMenu(); + appMenu = AvaloniaNativeMenuExporter.CreateDefaultAppMenu(); NativeMenu.SetMenu(Application.Current, appMenu); } diff --git a/src/Avalonia.Native/IAvnMenu.cs b/src/Avalonia.Native/IAvnMenu.cs index e413023f6d..f75621865c 100644 --- a/src/Avalonia.Native/IAvnMenu.cs +++ b/src/Avalonia.Native/IAvnMenu.cs @@ -111,7 +111,7 @@ namespace Avalonia.Native.Interop.Impl private __MicroComIAvnMenuItemProxy CreateNewAt(IAvaloniaNativeFactory factory, int index, NativeMenuItemBase item) { - var result = CreateNew(factory, item); + var result = __MicroComIAvnMenuProxy.CreateNew(factory, item); result.Initialize(item); @@ -123,7 +123,7 @@ namespace Avalonia.Native.Interop.Impl return result; } - private __MicroComIAvnMenuItemProxy CreateNew(IAvaloniaNativeFactory factory, NativeMenuItemBase item) + private static __MicroComIAvnMenuItemProxy CreateNew(IAvaloniaNativeFactory factory, NativeMenuItemBase item) { var nativeItem = (__MicroComIAvnMenuItemProxy)(item is NativeMenuItemSeparator ? factory.CreateMenuItemSeparator() : From 713b85522f34cc768a31ecf0fd8f28410d0ec1f6 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:11:19 +0200 Subject: [PATCH 012/173] feat(OpenGL): Address rule CA1822 --- src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs index 279e7e750d..6f87ff19ee 100644 --- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs @@ -27,7 +27,7 @@ namespace Avalonia.OpenGL.Controls _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb); EnsureTextureAttachment(); EnsureDepthBufferAttachment(_context.GlInterface); - if(!CheckFramebufferStatus(_context.GlInterface)) + if(!OpenGlControlBase.CheckFramebufferStatus(_context.GlInterface)) return; OnOpenGlRender(_context.GlInterface, _fb); @@ -38,7 +38,7 @@ namespace Avalonia.OpenGL.Controls base.Render(context); } - private void CheckError(GlInterface gl) + private static void CheckError(GlInterface gl) { int err; while ((err = gl.GetError()) != GL_NO_ERROR) @@ -186,7 +186,7 @@ namespace Avalonia.OpenGL.Controls EnsureDepthBufferAttachment(gl); EnsureTextureAttachment(); - return CheckFramebufferStatus(gl); + return OpenGlControlBase.CheckFramebufferStatus(gl); } catch(Exception e) { @@ -197,7 +197,7 @@ namespace Avalonia.OpenGL.Controls } } - private bool CheckFramebufferStatus(GlInterface gl) + private static bool CheckFramebufferStatus(GlInterface gl) { var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) From 5196ef2cbc559525051938e5714b8342771d6769 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:11:37 +0200 Subject: [PATCH 013/173] feat(Reactive): Address rule CA1822 --- .../AvaloniaActivationForViewFetcher.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs index 9f69b4ee6e..94073506e3 100644 --- a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs +++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs @@ -26,15 +26,15 @@ namespace Avalonia.ReactiveUI public IObservable GetActivationForView(IActivatableView view) { if (!(view is IVisual visual)) return Observable.Return(false); - if (view is Control control) return GetActivationForControl(control); - return GetActivationForVisual(visual); + if (view is Control control) return AvaloniaActivationForViewFetcher.GetActivationForControl(control); + return AvaloniaActivationForViewFetcher.GetActivationForVisual(visual); } /// /// Listens to Loaded and Unloaded /// events for Avalonia Control. /// - private IObservable GetActivationForControl(Control control) + private static IObservable GetActivationForControl(Control control) { var controlLoaded = Observable .FromEventPattern( @@ -55,7 +55,7 @@ namespace Avalonia.ReactiveUI /// Listens to AttachedToVisualTree and DetachedFromVisualTree /// events for Avalonia IVisuals. /// - private IObservable GetActivationForVisual(IVisual visual) + private static IObservable GetActivationForVisual(IVisual visual) { var visualLoaded = Observable .FromEventPattern( From a88ba2a16d1eecebb9177da5f8c41a0533068fa2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:11:53 +0200 Subject: [PATCH 014/173] feat(X11): Address rule CA1822 --- src/Avalonia.X11/X11IconLoader.cs | 6 +++--- src/Avalonia.X11/X11Platform.cs | 10 +++++----- src/Avalonia.X11/X11Window.Ime.cs | 2 +- src/Avalonia.X11/X11Window.cs | 6 +++--- src/Avalonia.X11/XI2Manager.cs | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index 632a7d39a2..de7c57a556 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -9,16 +9,16 @@ namespace Avalonia.X11 { class X11IconLoader : IPlatformIconLoader { - IWindowIconImpl LoadIcon(Bitmap bitmap) + static IWindowIconImpl LoadIcon(Bitmap bitmap) { var rv = new X11IconData(bitmap); bitmap.Dispose(); return rv; } - public IWindowIconImpl LoadIcon(string fileName) => LoadIcon(new Bitmap(fileName)); + public IWindowIconImpl LoadIcon(string fileName) => X11IconLoader.LoadIcon(new Bitmap(fileName)); - public IWindowIconImpl LoadIcon(Stream stream) => LoadIcon(new Bitmap(stream)); + public IWindowIconImpl LoadIcon(Stream stream) => X11IconLoader.LoadIcon(new Bitmap(stream)); public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) { diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 381cdd74c3..312945e713 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -42,10 +42,10 @@ namespace Avalonia.X11 Options = options; bool useXim = false; - if (EnableIme(options)) + if (AvaloniaX11Platform.EnableIme(options)) { // Attempt to configure DBus-based input method and check if we can fall back to XIM - if (!X11DBusImeHelper.DetectAndRegister() && ShouldUseXim()) + if (!X11DBusImeHelper.DetectAndRegister() && AvaloniaX11Platform.ShouldUseXim()) useXim = true; } @@ -143,7 +143,7 @@ namespace Avalonia.X11 throw new NotSupportedException(); } - bool EnableIme(X11PlatformOptions options) + static bool EnableIme(X11PlatformOptions options) { // Disable if explicitly asked by user var avaloniaImModule = Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE"); @@ -164,8 +164,8 @@ namespace Avalonia.X11 return isCjkLocale; } - - bool ShouldUseXim() + + static bool ShouldUseXim() { // Check if we are forbidden from using IME if (Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE") == "none" diff --git a/src/Avalonia.X11/X11Window.Ime.cs b/src/Avalonia.X11/X11Window.Ime.cs index d68feaca78..128d48957c 100644 --- a/src/Avalonia.X11/X11Window.Ime.cs +++ b/src/Avalonia.X11/X11Window.Ime.cs @@ -107,7 +107,7 @@ namespace Avalonia.X11 var filtered = ScheduleKeyInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev, (int)key, ev.KeyEvent.keycode); + X11KeyTransform.ConvertKey(key), X11Window.TranslateModifiers(ev.KeyEvent.state)), ref ev, (int)key, ev.KeyEvent.keycode); if (ev.type == XEventName.KeyPress && !filtered) TriggerClassicTextInputEvent(ref ev); diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index f24c33cafa..b2120b718e 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -463,7 +463,7 @@ namespace Avalonia.X11 : new Vector(-1, 0); ScheduleInput(new RawMouseWheelEventArgs(_mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), delta, - TranslateModifiers(ev.ButtonEvent.state)), ref ev); + X11Window.TranslateModifiers(ev.ButtonEvent.state)), ref ev); } } @@ -683,7 +683,7 @@ namespace Avalonia.X11 } - RawInputModifiers TranslateModifiers(XModifierMask state) + static RawInputModifiers TranslateModifiers(XModifierMask state) { var rv = default(RawInputModifiers); if (state.HasAllFlags(XModifierMask.Button1Mask)) @@ -760,7 +760,7 @@ namespace Avalonia.X11 { var mev = new RawPointerEventArgs( _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, - type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods)); + type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), X11Window.TranslateModifiers(mods)); ScheduleInput(mev, ref ev); } diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index 7bf1df41b6..952112b6fd 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -193,11 +193,11 @@ namespace Avalonia.X11 { var rev = (XIEnterLeaveEvent*)xev; if (_clients.TryGetValue(rev->EventWindow, out var client)) - OnEnterLeaveEvent(client, ref *rev); + XI2Manager.OnEnterLeaveEvent(client, ref *rev); } } - void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev) + static void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev) { if (ev.evtype == XiEventType.XI_Leave) { From 93f0fcf007c1e51556f25fd292a97b4375c9a3fd Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:12:14 +0200 Subject: [PATCH 015/173] feat(iOS): Address rule CA1822 --- src/iOS/Avalonia.iOS/CombinedSpan3.cs | 2 +- src/iOS/Avalonia.iOS/TouchHandler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iOS/Avalonia.iOS/CombinedSpan3.cs b/src/iOS/Avalonia.iOS/CombinedSpan3.cs index e9f44b7d58..13faea1848 100644 --- a/src/iOS/Avalonia.iOS/CombinedSpan3.cs +++ b/src/iOS/Avalonia.iOS/CombinedSpan3.cs @@ -16,7 +16,7 @@ internal ref struct CombinedSpan3 public int Length => Span1.Length + Span2.Length + Span3.Length; - void CopyFromSpan(ReadOnlySpan from, int offset, ref Span to) + static void CopyFromSpan(ReadOnlySpan from, int offset, ref Span to) { if(to.Length == 0) return; diff --git a/src/iOS/Avalonia.iOS/TouchHandler.cs b/src/iOS/Avalonia.iOS/TouchHandler.cs index 959a660d8a..44bf08365f 100644 --- a/src/iOS/Avalonia.iOS/TouchHandler.cs +++ b/src/iOS/Avalonia.iOS/TouchHandler.cs @@ -19,7 +19,7 @@ namespace Avalonia.iOS _tl = tl; } - ulong Ts(UIEvent evt) => (ulong) (evt.Timestamp * 1000); + static ulong Ts(UIEvent evt) => (ulong) (evt.Timestamp * 1000); private IInputRoot Root => _view.InputRoot; private static long _nextTouchPointId = 1; private Dictionary _knownTouches = new Dictionary(); From 4800f7a01b666d962e2f5103ff39dce0e98fea18 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 14 Oct 2022 17:14:27 +0200 Subject: [PATCH 016/173] feat(Markup): Address rule CA1822 --- .../CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs | 6 ++---- .../Converters/AvaloniaPropertyTypeConverter.cs | 8 ++++---- .../Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs | 4 ++-- .../MarkupExtensions/DynamicResourceExtension.cs | 6 +++--- src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs | 2 +- .../Markup/Parsers/Nodes/StringIndexerNode.cs | 6 +++--- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 6c9d510ba0..12406df765 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -67,10 +67,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions string propertyName, IXamlAstTypeReference selectorTypeReference, IXamlLineInfo lineInfo) { XamlAstNamePropertyReference forgedReference; - - var parser = new PropertyParser(); - - var parsedPropertyName = parser.Parse(new CharacterReader(propertyName.AsSpan())); + + var parsedPropertyName = PropertyParser.Parse(new CharacterReader(propertyName.AsSpan())); if(parsedPropertyName.owner == null) forgedReference = new XamlAstNamePropertyReference(lineInfo, selectorTypeReference, propertyName, selectorTypeReference); diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs index 45ca1c4adc..dff315c727 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs @@ -22,8 +22,8 @@ namespace Avalonia.Markup.Xaml.Converters { var registry = AvaloniaPropertyRegistry.Instance; var parser = new PropertyParser(); - var (ns, owner, propertyName) = parser.Parse(new CharacterReader(((string)value).AsSpan())); - var ownerType = TryResolveOwnerByName(context, ns, owner); + var (ns, owner, propertyName) = PropertyParser.Parse(new CharacterReader(((string)value).AsSpan())); + var ownerType = AvaloniaPropertyTypeConverter.TryResolveOwnerByName(context, ns, owner); var targetType = context.GetFirstParent()?.TargetType ?? context.GetFirstParent"; - using (StartWithResources(("test:style.xaml", styleXaml))) + using (StyleIncludeTests.StartWithResources(("test:style.xaml", styleXaml))) { var xaml = @" new Styles { - WindowStyle(), + ResourceDictionaryTests.WindowStyle(), }); return UnitTestApplication.Start(services); } - private Style WindowStyle() + private static Style WindowStyle() { return new Style(x => x.OfType()) { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 77a4932ccc..b164f6c371 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -162,7 +162,7 @@ namespace Avalonia.Markup.Xaml.UnitTests } - void AssertThrows(Action callback, Func check) + static void AssertThrows(Action callback, Func check) { try { @@ -182,7 +182,7 @@ namespace Avalonia.Markup.Xaml.UnitTests public void Bug2570() { SomeStaticProperty = "123"; - AssertThrows(() => AvaloniaRuntimeXamlLoader + XamlIlTests.AssertThrows(() => AvaloniaRuntimeXamlLoader .Load(@" (child); } - private FuncControlTemplate GetTemplate() + private static FuncControlTemplate GetTemplate() { return new FuncControlTemplate((parent, scope) => { diff --git a/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs b/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs index 06e46c1a06..f6e7ba0c48 100644 --- a/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs +++ b/tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs @@ -17,7 +17,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media { } - private GeometryDrawing CreateGeometryDrawing() + private static GeometryDrawing CreateGeometryDrawing() { GeometryDrawing geometryDrawing = new GeometryDrawing(); EllipseGeometry ellipse = new EllipseGeometry(); diff --git a/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs index b668f4d39e..f921d9fa64 100644 --- a/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs @@ -39,7 +39,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media { } - private TextLayout Create(string text, + private static TextLayout Create(string text, string fontFamily, double fontSize, FontStyle fontStyle, @@ -56,7 +56,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media return formattedText; } - private TextLayout Create(string text, double fontSize) + private static TextLayout Create(string text, double fontSize) { return Create(text, FontName, fontSize, FontStyle.Normal, TextAlignment.Left, @@ -64,7 +64,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media -1); } - private TextLayout Create(string text, double fontSize, TextAlignment alignment, double widthConstraint) + private static TextLayout Create(string text, double fontSize, TextAlignment alignment, double widthConstraint) { return Create(text, FontName, fontSize, FontStyle.Normal, alignment, @@ -72,7 +72,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media widthConstraint); } - private TextLayout Create(string text, double fontSize, TextWrapping wrap, double widthConstraint) + private static TextLayout Create(string text, double fontSize, TextWrapping wrap, double widthConstraint) { return Create(text, FontName, fontSize, FontStyle.Normal, TextAlignment.Left, diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 4d6b313ffc..3f918e2a73 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -237,7 +237,7 @@ namespace Avalonia.Direct2D1.RenderTests return Math.Sqrt(meanSquaresError); } - private string GetTestsDirectory() + private static string GetTestsDirectory() { var path = Directory.GetCurrentDirectory(); diff --git a/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs b/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs index df0cc2fc1a..68595a2f57 100644 --- a/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs +++ b/tests/Avalonia.Skia.UnitTests/DrawingContextImplTests.cs @@ -21,7 +21,7 @@ namespace Avalonia.Skia.UnitTests target.DrawRectangle(Brushes.Black, new Pen(Brushes.Black, 0), new RoundedRect(new Rect(0, 0, 100, 100), new CornerRadius(4))); } - private DrawingContextImpl CreateTarget() + private static DrawingContextImpl CreateTarget() { var canvas = new SKCanvas(new SKBitmap(100, 100)); return (DrawingContextImpl)DrawingContextHelper.WrapSkiaCanvas(canvas, new Vector(96, 96)); From 9e1ff3e3be9a855220153ba72a0dd991e41de19b Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 17 Oct 2022 16:38:15 +0200 Subject: [PATCH 021/173] fix: Addreaa review --- .../Animators/GradientBrushAnimator.cs | 6 +++--- src/Avalonia.Base/Animation/KeySpline.cs | 6 +++--- src/Avalonia.Base/Controls/Classes.cs | 18 +++++++++--------- src/Avalonia.Base/Data/Core/ExpressionNode.cs | 2 +- .../Parsers/ExpressionVisitorNodeBuilder.cs | 6 +++--- .../Plugins/DataAnnotationsValidationPlugin.cs | 2 +- .../Data/Core/Plugins/IndeiValidationPlugin.cs | 2 +- .../Data/Core/Plugins/TaskStreamPlugin.cs | 4 ++-- src/Avalonia.Base/Input/DragDropDevice.cs | 12 ++++++------ src/Avalonia.Base/Input/KeyGesture.cs | 2 +- src/Avalonia.Base/Input/KeyboardDevice.cs | 4 ++-- src/Avalonia.Base/Input/MouseDevice.cs | 11 ++++------- src/Avalonia.Base/Input/PenDevice.cs | 2 +- src/Avalonia.Base/Input/Pointer.cs | 4 ++-- src/Avalonia.Base/Input/TouchDevice.cs | 6 +++--- src/Avalonia.Base/Layout/AttachedLayout.cs | 8 ++++---- src/Avalonia.Base/Layout/StackLayout.cs | 8 ++++---- src/Avalonia.Base/Layout/UniformGridLayout.cs | 6 +++--- src/Avalonia.Base/Platform/AssetLoader.cs | 8 ++++---- .../Composition/CompositingRenderer.cs | 4 ++-- .../Rendering/Composition/CompositionTarget.cs | 6 +++--- .../Automation/Peers/ComboBoxAutomationPeer.cs | 6 +++--- .../CalendarBlackoutDatesCollection.cs | 8 ++++---- .../Calendar/SelectedDatesCollection.cs | 9 ++++----- src/Avalonia.Controls/ComboBox.cs | 14 +++++++------- .../DateTimePickers/DateTimePickerPanel.cs | 4 ++-- src/Avalonia.Controls/Grid.cs | 10 +++++----- src/Avalonia.Controls/GridSplitter.cs | 14 +++++++------- .../Platform/InProcessDragSource.cs | 4 ++-- .../Presenters/ScrollContentPresenter.cs | 8 ++++---- .../Primitives/AdornerLayer.cs | 8 ++++---- src/Avalonia.Controls/Primitives/Popup.cs | 6 ++---- src/Avalonia.Controls/Repeater/RecyclePool.cs | 4 ++-- .../Repeater/RecyclingElementFactory.cs | 6 +++--- src/Avalonia.Controls/SplitView.cs | 9 +++------ src/Avalonia.Controls/TextBox.cs | 9 ++++----- src/Avalonia.Controls/TopLevel.cs | 2 +- src/Avalonia.Controls/TreeView.cs | 6 ++---- .../Screenshots/FilePickerHandler.cs | 2 +- .../ViewModels/ControlDetailsViewModel.cs | 16 ++++++++-------- .../LinuxMountedVolumeInfoListener.cs | 8 ++++---- .../AvaloniaNativeDragSource.cs | 2 +- .../AvaloniaNativeMenuExporter.cs | 2 +- src/Avalonia.Native/IAvnMenu.cs | 3 +-- .../Controls/OpenGlControlBase.cs | 4 ++-- .../AvaloniaActivationForViewFetcher.cs | 4 ++-- src/Avalonia.X11/X11IconLoader.cs | 4 ++-- src/Avalonia.X11/X11Platform.cs | 7 +++---- src/Avalonia.X11/X11Window.Ime.cs | 3 +-- src/Avalonia.X11/X11Window.cs | 6 ++---- src/Avalonia.X11/XI2Manager.cs | 2 +- .../XamlIlAvaloniaPropertyHelper.cs | 2 -- .../Converters/IconTypeConverter.cs | 2 +- .../DynamicResourceExtension.cs | 4 ++-- .../Parsers/PropertyParser.cs | 4 +--- .../Markup/Parsers/Nodes/StringIndexerNode.cs | 8 ++++---- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 4 ++-- src/Windows/Avalonia.Win32/ClipboardImpl.cs | 6 +++--- src/Windows/Avalonia.Win32/CursorFactory.cs | 2 +- src/Windows/Avalonia.Win32/DataObject.cs | 8 ++++---- .../CompositionGenerator/Generator.cs | 2 +- 61 files changed, 169 insertions(+), 190 deletions(-) diff --git a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs index f469ea5652..068c190fa1 100644 --- a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs @@ -28,7 +28,7 @@ namespace Avalonia.Animation.Animators { case IRadialGradientBrush oldRadial when newValue is IRadialGradientBrush newRadial: return new ImmutableRadialGradientBrush( - GradientBrushAnimator.InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), @@ -39,7 +39,7 @@ namespace Avalonia.Animation.Animators case IConicGradientBrush oldConic when newValue is IConicGradientBrush newConic: return new ImmutableConicGradientBrush( - GradientBrushAnimator.InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), @@ -49,7 +49,7 @@ namespace Avalonia.Animation.Animators case ILinearGradientBrush oldLinear when newValue is ILinearGradientBrush newLinear: return new ImmutableLinearGradientBrush( - GradientBrushAnimator.InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), + InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), diff --git a/src/Avalonia.Base/Animation/KeySpline.cs b/src/Avalonia.Base/Animation/KeySpline.cs index b33cd6b881..6ca5b2e759 100644 --- a/src/Avalonia.Base/Animation/KeySpline.cs +++ b/src/Avalonia.Base/Animation/KeySpline.cs @@ -98,7 +98,7 @@ namespace Avalonia.Animation get => _controlPointX1; set { - if (KeySpline.IsValidXValue(value)) + if (IsValidXValue(value)) { _controlPointX1 = value; _isDirty = true; @@ -131,7 +131,7 @@ namespace Avalonia.Animation get => _controlPointX2; set { - if (KeySpline.IsValidXValue(value)) + if (IsValidXValue(value)) { _controlPointX2 = value; _isDirty = true; @@ -188,7 +188,7 @@ namespace Avalonia.Animation /// acceptable range; false otherwise. public bool IsValid() { - return KeySpline.IsValidXValue(_controlPointX1) && KeySpline.IsValidXValue(_controlPointX2); + return IsValidXValue(_controlPointX1) && IsValidXValue(_controlPointX2); } /// diff --git a/src/Avalonia.Base/Controls/Classes.cs b/src/Avalonia.Base/Controls/Classes.cs index 100c2b2a8f..04193cf3d9 100644 --- a/src/Avalonia.Base/Controls/Classes.cs +++ b/src/Avalonia.Base/Controls/Classes.cs @@ -37,7 +37,7 @@ namespace Avalonia.Controls /// The initial items. public Classes(params string[] items) : base(items) - { + { } /// @@ -63,7 +63,7 @@ namespace Avalonia.Controls /// public override void Add(string name) { - Classes.ThrowIfPseudoclass(name, "added"); + ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -87,7 +87,7 @@ namespace Avalonia.Controls foreach (var name in names) { - Classes.ThrowIfPseudoclass(name, "added"); + ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -127,7 +127,7 @@ namespace Avalonia.Controls /// public override void Insert(int index, string name) { - Classes.ThrowIfPseudoclass(name, "added"); + ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -152,7 +152,7 @@ namespace Avalonia.Controls foreach (var name in names) { - Classes.ThrowIfPseudoclass(name, "added"); + ThrowIfPseudoclass(name, "added"); if (!Contains(name)) { @@ -180,7 +180,7 @@ namespace Avalonia.Controls /// public override bool Remove(string name) { - Classes.ThrowIfPseudoclass(name, "removed"); + ThrowIfPseudoclass(name, "removed"); if (base.Remove(name)) { @@ -206,7 +206,7 @@ namespace Avalonia.Controls foreach (var name in names) { - Classes.ThrowIfPseudoclass(name, "removed"); + ThrowIfPseudoclass(name, "removed"); toRemove ??= new List(); @@ -232,7 +232,7 @@ namespace Avalonia.Controls public override void RemoveAt(int index) { var name = this[index]; - Classes.ThrowIfPseudoclass(name, "removed"); + ThrowIfPseudoclass(name, "removed"); base.RemoveAt(index); NotifyChanged(); } @@ -258,7 +258,7 @@ namespace Avalonia.Controls foreach (var name in source) { - Classes.ThrowIfPseudoclass(name, "added"); + ThrowIfPseudoclass(name, "added"); } foreach (var name in this) diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index d1bc60541c..e4b833176c 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -138,7 +138,7 @@ namespace Avalonia.Data.Core if (target == null) { - ValueChanged(ExpressionNode.TargetNullNotification()); + ValueChanged(TargetNullNotification()); _listening = false; } else if (target != AvaloniaProperty.UnsetValue) diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs index 9b9ddf2183..42aefb3f54 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs +++ b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs @@ -70,7 +70,7 @@ namespace Avalonia.Data.Core.Parsers if (node.Indexer == AvaloniaObjectIndexer) { - var property = ExpressionVisitorNodeBuilder.GetArgumentExpressionValue(node.Arguments[0]); + var property = GetArgumentExpressionValue(node.Arguments[0]); Nodes.Add(new AvaloniaPropertyAccessorNode(property, _enableDataValidation)); } else @@ -162,7 +162,7 @@ namespace Avalonia.Data.Core.Parsers if (node.Method == CreateDelegateMethod) { var visited = Visit(node.Arguments[1]); - Nodes.Add(new PropertyAccessorNode(ExpressionVisitorNodeBuilder.GetArgumentExpressionValue(node.Object!).Name, _enableDataValidation)); + Nodes.Add(new PropertyAccessorNode(GetArgumentExpressionValue(node.Object!).Name, _enableDataValidation)); return node; } else if (node.Method.Name == StreamBindingExtensions.StreamBindingName || node.Method.Name.StartsWith(StreamBindingExtensions.StreamBindingName + '`')) @@ -193,7 +193,7 @@ namespace Avalonia.Data.Core.Parsers throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType!.AssemblyQualifiedName}.{node.Method.Name}'."); } - private PropertyInfo? TryGetPropertyFromMethod(MethodInfo method) + private static PropertyInfo? TryGetPropertyFromMethod(MethodInfo method) { var type = method.DeclaringType; return type?.GetRuntimeProperties().FirstOrDefault(prop => prop.GetMethod == method); diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs index 54d5b5ac28..118b18c020 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs @@ -57,7 +57,7 @@ namespace Avalonia.Data.Core.Plugins else { base.InnerValueChanged(new BindingNotification( - Accessor.CreateException(errors), + CreateException(errors), BindingErrorType.DataValidationError, value)); } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs index e45170ff7e..385d96a7b8 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs @@ -92,7 +92,7 @@ namespace Avalonia.Data.Core.Plugins if (errors?.Count > 0) { return new BindingNotification( - Validator.GenerateException(errors), + GenerateException(errors), BindingErrorType.DataValidationError, value); } diff --git a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs index b25d592597..6703d1f54e 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs @@ -44,11 +44,11 @@ namespace Avalonia.Data.Core.Plugins { case TaskStatus.RanToCompletion: case TaskStatus.Faulted: - return TaskStreamPlugin.HandleCompleted(task); + return HandleCompleted(task); default: var subject = new Subject(); task.ContinueWith( - x => TaskStreamPlugin.HandleCompleted(task).Subscribe(subject), + x => HandleCompleted(task).Subscribe(subject), TaskScheduler.FromCurrentSynchronizationContext()) .ConfigureAwait(false); return subject; diff --git a/src/Avalonia.Base/Input/DragDropDevice.cs b/src/Avalonia.Base/Input/DragDropDevice.cs index 16ff428d69..ef4c7f8787 100644 --- a/src/Avalonia.Base/Input/DragDropDevice.cs +++ b/src/Avalonia.Base/Input/DragDropDevice.cs @@ -40,22 +40,22 @@ namespace Avalonia.Input private DragDropEffects DragEnter(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { - _lastTarget = DragDropDevice.GetTarget(inputRoot, point); - return DragDropDevice.RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); + _lastTarget = GetTarget(inputRoot, point); + return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } private DragDropEffects DragOver(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers) { - var target = DragDropDevice.GetTarget(inputRoot, point); + var target = GetTarget(inputRoot, point); if (target == _lastTarget) - return DragDropDevice.RaiseDragEvent(target, inputRoot, point, DragDrop.DragOverEvent, effects, data, modifiers); + return RaiseDragEvent(target, inputRoot, point, DragDrop.DragOverEvent, effects, data, modifiers); try { if (_lastTarget != null) _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent)); - return DragDropDevice.RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); + return RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers); } finally { @@ -81,7 +81,7 @@ namespace Avalonia.Input { try { - return DragDropDevice.RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DropEvent, effects, data, modifiers); + return RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DropEvent, effects, data, modifiers); } finally { diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 7f4f69590a..b08f5d0ba5 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -138,7 +138,7 @@ namespace Avalonia.Input public bool Matches(KeyEventArgs keyEvent) => keyEvent != null && keyEvent.KeyModifiers == KeyModifiers && - KeyGesture.ResolveNumPadOperationKey(keyEvent.Key) == KeyGesture.ResolveNumPadOperationKey(Key); + ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key); // TODO: Move that to external key parser private static Key ParseKey(string key) diff --git a/src/Avalonia.Base/Input/KeyboardDevice.cs b/src/Avalonia.Base/Input/KeyboardDevice.cs index 0600b54618..68d09ea19a 100644 --- a/src/Avalonia.Base/Input/KeyboardDevice.cs +++ b/src/Avalonia.Base/Input/KeyboardDevice.cs @@ -26,7 +26,7 @@ namespace Avalonia.Input public IInputElement? FocusedElement => _focusedElement; - private void ClearFocusWithinAncestors(IInputElement? element) + private static void ClearFocusWithinAncestors(IInputElement? element) { var el = element; @@ -65,7 +65,7 @@ namespace Avalonia.Input { if (newElement == null && oldElement != null) { - ClearFocusWithinAncestors(oldElement); + KeyboardDevice.ClearFocusWithinAncestors(oldElement); return; } diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 6fb34efdb5..a7540b5afa 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.VisualTree; namespace Avalonia.Input { @@ -71,7 +67,7 @@ namespace Avalonia.Input case RawPointerEventType.MiddleButtonDown: case RawPointerEventType.XButton1Down: case RawPointerEventType.XButton2Down: - if (MouseDevice.ButtonCount(props) > 1) + if (ButtonCount(props) > 1) e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); @@ -81,7 +77,7 @@ namespace Avalonia.Input case RawPointerEventType.MiddleButtonUp: case RawPointerEventType.XButton1Up: case RawPointerEventType.XButton2Up: - if (MouseDevice.ButtonCount(props) != 0) + if (ButtonCount(props) != 0) e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); @@ -106,9 +102,10 @@ namespace Avalonia.Input private void LeaveWindow() { + } - PointerPointProperties CreateProperties(RawPointerEventArgs args) + static PointerPointProperties CreateProperties(RawPointerEventArgs args) { return new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()); } diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index 876be42be8..b530364fc1 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -56,7 +56,7 @@ namespace Avalonia.Input e.Handled = PenUp(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Move: - e.Handled = PenDevice.PenMove(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult, e.IntermediatePoints); + e.Handled = PenMove(pointer, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult, e.IntermediatePoints); break; } diff --git a/src/Avalonia.Base/Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs index be93d9d6b8..7948939d41 100644 --- a/src/Avalonia.Base/Input/Pointer.cs +++ b/src/Avalonia.Base/Input/Pointer.cs @@ -41,7 +41,7 @@ namespace Avalonia.Input PlatformCapture(control); if (oldCapture != null) { - var commonParent = Pointer.FindCommonParent(control, oldCapture); + var commonParent = FindCommonParent(control, oldCapture); foreach (var notifyTarget in oldCapture.GetSelfAndVisualAncestors().OfType()) { if (notifyTarget == commonParent) @@ -54,7 +54,7 @@ namespace Avalonia.Input Captured.DetachedFromVisualTree += OnCaptureDetached; } - IInputElement? GetNextCapture(IVisual parent) + static IInputElement? GetNextCapture(IVisual parent) { return parent as IInputElement ?? parent.FindAncestorOfType(); } diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index b709b7d0cd..35c7555784 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -73,7 +73,7 @@ namespace Avalonia.Input target.RaiseEvent(new PointerPressedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(TouchDevice.GetModifiers(args.InputModifiers, true), updateKind), + new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), keyModifier, _clickCount)); } @@ -84,7 +84,7 @@ namespace Avalonia.Input { target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(TouchDevice.GetModifiers(args.InputModifiers, false), updateKind), + new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), keyModifier, MouseButton.Left)); } } @@ -100,7 +100,7 @@ namespace Avalonia.Input { target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(TouchDevice.GetModifiers(args.InputModifiers, true), updateKind), + new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), keyModifier, args.IntermediatePoints)); } } diff --git a/src/Avalonia.Base/Layout/AttachedLayout.cs b/src/Avalonia.Base/Layout/AttachedLayout.cs index 594fc04842..ac3a53dabb 100644 --- a/src/Avalonia.Base/Layout/AttachedLayout.cs +++ b/src/Avalonia.Base/Layout/AttachedLayout.cs @@ -67,7 +67,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); + var virtualizingContext = GetVirtualizingLayoutContext(context); virtualizingLayout.InitializeForContextCore(virtualizingContext); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -92,7 +92,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); + var virtualizingContext = GetVirtualizingLayoutContext(context); virtualizingLayout.UninitializeForContextCore(virtualizingContext); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -126,7 +126,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); + var virtualizingContext = GetVirtualizingLayoutContext(context); return virtualizingLayout.MeasureOverride(virtualizingContext, availableSize); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) @@ -157,7 +157,7 @@ namespace Avalonia.Layout { if (this is VirtualizingLayout virtualizingLayout) { - var virtualizingContext = AttachedLayout.GetVirtualizingLayoutContext(context); + var virtualizingContext = GetVirtualizingLayoutContext(context); return virtualizingLayout.ArrangeOverride(virtualizingContext, finalSize); } else if (this is NonVirtualizingLayout nonVirtualizingLayout) diff --git a/src/Avalonia.Base/Layout/StackLayout.cs b/src/Avalonia.Base/Layout/StackLayout.cs index 7983b37843..d36667a7f2 100644 --- a/src/Avalonia.Base/Layout/StackLayout.cs +++ b/src/Avalonia.Base/Layout/StackLayout.cs @@ -90,7 +90,7 @@ namespace Avalonia.Layout // Constants int itemsCount = context.ItemCount; var stackState = (StackLayoutState)context.LayoutState!; - double averageElementSize = StackLayout.GetAverageElementSize(availableSize, context, stackState) + Spacing; + double averageElementSize = GetAverageElementSize(availableSize, context, stackState) + Spacing; _orientation.SetMinorSize(ref extent, stackState.MaxArrangeBounds); _orientation.SetMajorSize(ref extent, Math.Max(0.0f, itemsCount * averageElementSize - Spacing)); @@ -178,7 +178,7 @@ namespace Avalonia.Layout { index = targetIndex; var state = (StackLayoutState)context.LayoutState!; - double averageElementSize = StackLayout.GetAverageElementSize(availableSize, context, state) + Spacing; + double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; offset = index * averageElementSize + _orientation.MajorStart(state.FlowAlgorithm.LastExtent); } @@ -237,7 +237,7 @@ namespace Avalonia.Layout var state = (StackLayoutState)context.LayoutState!; var lastExtent = state.FlowAlgorithm.LastExtent; - double averageElementSize = StackLayout.GetAverageElementSize(availableSize, context, state) + Spacing; + double averageElementSize = GetAverageElementSize(availableSize, context, state) + Spacing; double realizationWindowOffsetInExtent = _orientation.MajorStart(realizationRect) - _orientation.MajorStart(lastExtent); double majorSize = _orientation.MajorSize(lastExtent) == 0 ? Math.Max(0.0, averageElementSize * itemsCount - Spacing) : _orientation.MajorSize(lastExtent); if (itemsCount > 0 && @@ -359,6 +359,6 @@ namespace Avalonia.Layout private void InvalidateLayout() => InvalidateMeasure(); - private FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm; + private static FlowLayoutAlgorithm GetFlowAlgorithm(VirtualizingLayoutContext context) => ((StackLayoutState)context.LayoutState!).FlowAlgorithm; } } diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs index acb333bcfa..407d115bcd 100644 --- a/src/Avalonia.Base/Layout/UniformGridLayout.cs +++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs @@ -432,7 +432,7 @@ namespace Avalonia.Layout var gridState = (UniformGridLayoutState)context.LayoutState!; gridState.EnsureElementSize(availableSize, context, _minItemWidth, _minItemHeight, _itemsStretch, Orientation, MinRowSpacing, MinColumnSpacing, _maximumRowsOrColumns); - var desiredSize = UniformGridLayout.GetFlowAlgorithm(context).Measure( + var desiredSize = GetFlowAlgorithm(context).Measure( availableSize, context, true, @@ -452,7 +452,7 @@ namespace Avalonia.Layout protected internal override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) { - var value = UniformGridLayout.GetFlowAlgorithm(context).Arrange( + var value = GetFlowAlgorithm(context).Arrange( finalSize, context, true, @@ -463,7 +463,7 @@ namespace Avalonia.Layout protected internal override void OnItemsChangedCore(VirtualizingLayoutContext context, object? source, NotifyCollectionChangedEventArgs args) { - UniformGridLayout.GetFlowAlgorithm(context).OnItemsSourceChanged(source, args, context); + GetFlowAlgorithm(context).OnItemsSourceChanged(source, args, context); // Always invalidate layout to keep the view accurate. InvalidateLayout(); diff --git a/src/Avalonia.Base/Platform/AssetLoader.cs b/src/Avalonia.Base/Platform/AssetLoader.cs index 77ae9f4c32..a08e3b3d4c 100644 --- a/src/Avalonia.Base/Platform/AssetLoader.cs +++ b/src/Avalonia.Base/Platform/AssetLoader.cs @@ -126,7 +126,7 @@ namespace Avalonia.Platform uri = uri.EnsureAbsolute(baseUri); if (uri.IsAvares()) { - var (asm, path) = AssetLoader.GetResAsmAndPath(uri); + var (asm, path) = GetResAsmAndPath(uri); if (asm == null) { throw new ArgumentException( @@ -171,7 +171,7 @@ namespace Avalonia.Platform if (uri.IsAvares()) { - var (asm, path) = AssetLoader.GetResAsmAndPath(uri); + var (asm, path) = GetResAsmAndPath(uri); if (asm.AvaloniaResources == null) return null; asm.AvaloniaResources.TryGetValue(path, out var desc); @@ -187,14 +187,14 @@ namespace Avalonia.Platform return (asm, uri.GetUnescapeAbsolutePath()); } - private IAssemblyDescriptor? GetAssembly(Uri? uri) + private static IAssemblyDescriptor? GetAssembly(Uri? uri) { if (uri != null) { if (!uri.IsAbsoluteUri) return null; if (uri.IsAvares()) - return AssetLoader.GetResAsmAndPath(uri).asm; + return GetResAsmAndPath(uri).asm; if (uri.IsResm()) { diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 3c9a9feac0..3f145c1539 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -233,11 +233,11 @@ public class CompositingRenderer : IRendererWithCompositor visual.Render(_recordingContext); comp.DrawList = _recorder.EndUpdate(); - CompositingRenderer.SyncChildren(visual); + SyncChildren(visual); } foreach(var v in _recalculateChildren) if (!_dirty.Contains(v)) - CompositingRenderer.SyncChildren(v); + SyncChildren(v); _dirty.Clear(); _recalculateChildren.Clear(); CompositionTarget.Size = _root.ClientSize; diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs index 52215e8011..0c24d6cd44 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs @@ -53,7 +53,7 @@ namespace Avalonia.Rendering.Composition var m = Matrix.Identity; while (v != null) { - if (!CompositionTarget.TryGetInvertedTransform(v, out var cm)) + if (!TryGetInvertedTransform(v, out var cm)) return null; m = m * cm; v = v.Parent; @@ -75,10 +75,10 @@ namespace Avalonia.Rendering.Composition return m33.TryInvert(out matrix); } - bool TryTransformTo(CompositionVisual visual, Point globalPoint, out Point v) + static bool TryTransformTo(CompositionVisual visual, Point globalPoint, out Point v) { v = default; - if (CompositionTarget.TryGetInvertedTransform(visual, out var m)) + if (TryGetInvertedTransform(visual, out var m)) { v = globalPoint * m; return true; diff --git a/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs index d6295fdbd9..5d71e7a8e4 100644 --- a/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs @@ -18,7 +18,7 @@ namespace Avalonia.Automation.Peers public new ComboBox Owner => (ComboBox)base.Owner; - public ExpandCollapseState ExpandCollapseState => ComboBoxAutomationPeer.ToState(Owner.IsDropDownOpen); + public ExpandCollapseState ExpandCollapseState => ToState(Owner.IsDropDownOpen); public bool ShowsMenu => true; public void Collapse() => Owner.IsDropDownOpen = false; public void Expand() => Owner.IsDropDownOpen = true; @@ -66,8 +66,8 @@ namespace Avalonia.Automation.Peers { RaisePropertyChangedEvent( ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, - ComboBoxAutomationPeer.ToState((bool)e.OldValue!), - ComboBoxAutomationPeer.ToState((bool)e.NewValue!)); + ToState((bool)e.OldValue!), + ToState((bool)e.NewValue!)); } } diff --git a/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs b/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs index 61463795ee..fe8b616e02 100644 --- a/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs @@ -122,7 +122,7 @@ namespace Avalonia.Controls.Primitives /// protected override void ClearItems() { - CalendarBlackoutDatesCollection.EnsureValidThread(); + EnsureValidThread(); base.ClearItems(); _owner.UpdateMonths(); @@ -140,7 +140,7 @@ namespace Avalonia.Controls.Primitives /// protected override void InsertItem(int index, CalendarDateRange item) { - CalendarBlackoutDatesCollection.EnsureValidThread(); + EnsureValidThread(); if (!IsValid(item)) { @@ -162,7 +162,7 @@ namespace Avalonia.Controls.Primitives /// protected override void RemoveItem(int index) { - CalendarBlackoutDatesCollection.EnsureValidThread(); + EnsureValidThread(); base.RemoveItem(index); _owner.UpdateMonths(); @@ -182,7 +182,7 @@ namespace Avalonia.Controls.Primitives /// protected override void SetItem(int index, CalendarDateRange item) { - CalendarBlackoutDatesCollection.EnsureValidThread(); + EnsureValidThread(); if (!IsValid(item)) { diff --git a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs index 8327442fcf..ac4159d536 100644 --- a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs @@ -6,7 +6,6 @@ using Avalonia.Threading; using System; using System.Collections.ObjectModel; -using System.Threading; namespace Avalonia.Controls.Primitives { @@ -133,7 +132,7 @@ namespace Avalonia.Controls.Primitives /// protected override void ClearItems() { - SelectedDatesCollection.EnsureValidThread(); + EnsureValidThread(); Collection addedItems = new Collection(); Collection removedItems = new Collection(); @@ -170,7 +169,7 @@ namespace Avalonia.Controls.Primitives /// protected override void InsertItem(int index, DateTime item) { - SelectedDatesCollection.EnsureValidThread(); + EnsureValidThread(); if (!Contains(item)) { @@ -233,7 +232,7 @@ namespace Avalonia.Controls.Primitives /// protected override void RemoveItem(int index) { - SelectedDatesCollection.EnsureValidThread(); + EnsureValidThread(); if (index >= Count) { @@ -284,7 +283,7 @@ namespace Avalonia.Controls.Primitives /// protected override void SetItem(int index, DateTime item) { - SelectedDatesCollection.EnsureValidThread(); + EnsureValidThread(); if (!Contains(item)) { diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 21918b27a8..1d849ce1e3 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -96,7 +96,7 @@ namespace Avalonia.Controls ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); FocusableProperty.OverrideDefaultValue(true); SelectedItemProperty.Changed.AddClassHandler((x, e) => x.SelectedItemChanged(e)); - KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel); + KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e), RoutingStrategies.Tunnel); IsTextSearchEnabledProperty.OverrideDefaultValue(true); IsDropDownOpenProperty.Changed.AddClassHandler((x, e) => x.DropdownChanged(e)); } @@ -178,8 +178,8 @@ namespace Avalonia.Controls { return new ItemContainerGenerator( this, - ComboBoxItem.ContentProperty, - ComboBoxItem.ContentTemplateProperty); + ContentControl.ContentProperty, + ContentControl.ContentTemplateProperty); } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) @@ -236,7 +236,7 @@ namespace Avalonia.Controls else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 && (e.Key == Key.Up || e.Key == Key.Down) && IsFocused == true) { - var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => ComboBox.CanFocus(c)); + var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c)); if (firstChild != null) { FocusManager.Instance?.Focus(firstChild, NavigationMethod.Directional); @@ -341,7 +341,7 @@ namespace Avalonia.Controls { _subscriptionsOnOpen.Clear(); - if (ComboBox.CanFocus(this)) + if (CanFocus(this)) { Focus(); } @@ -363,7 +363,7 @@ namespace Avalonia.Controls { ev.Handled = true; } - }, Interactivity.RoutingStrategies.Tunnel).DisposeWith(_subscriptionsOnOpen); + }, RoutingStrategies.Tunnel).DisposeWith(_subscriptionsOnOpen); } this.GetObservable(IsVisibleProperty).Subscribe(IsVisibleChanged).DisposeWith(_subscriptionsOnOpen); @@ -403,7 +403,7 @@ namespace Avalonia.Controls container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); } - if (container != null && ComboBox.CanFocus(container)) + if (container != null && CanFocus(container)) { container.Focus(); } diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index eb4a7391b0..faf0b6415a 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -455,7 +455,7 @@ namespace Avalonia.Controls.Primitives { Height = ItemHeight, Classes = new Classes($"{PanelType}Item"), - VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center, + VerticalContentAlignment = Layout.VerticalAlignment.Center, Focusable = false }); } @@ -546,7 +546,7 @@ namespace Avalonia.Controls.Primitives private void OnItemTapped(object? sender, TappedEventArgs e) { if (e.Source is IVisual source && - DateTimePickerPanel.GetItemFromSource(source) is ListBoxItem listBoxItem && + GetItemFromSource(source) is ListBoxItem listBoxItem && listBoxItem.Tag is int tag) { SelectedValue = tag; diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 4dc88fec6a..7737fdac2e 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -1117,7 +1117,7 @@ namespace Avalonia.Controls else { // otherwise... - cellMeasureWidth = Grid.GetMeasureSizeForRange( + cellMeasureWidth = GetMeasureSizeForRange( DefinitionsU, PrivateCells[cell].ColumnIndex, PrivateCells[cell].ColumnSpan); @@ -1137,7 +1137,7 @@ namespace Avalonia.Controls } else { - cellMeasureHeight = Grid.GetMeasureSizeForRange( + cellMeasureHeight = GetMeasureSizeForRange( DefinitionsV, PrivateCells[cell].RowIndex, PrivateCells[cell].RowSpan); @@ -1192,7 +1192,7 @@ namespace Avalonia.Controls /// Starting index of the range. /// Number of definitions included in the range. /// Length type for given range. - private LayoutTimeSizeType GetLengthTypeForRange( + private static LayoutTimeSizeType GetLengthTypeForRange( IReadOnlyList definitions, int start, int count) @@ -1721,7 +1721,7 @@ namespace Avalonia.Controls /// /// Array of definitions to use for calculations. /// Desired size. - private double CalculateDesiredSize( + private static double CalculateDesiredSize( IReadOnlyList definitions) { double desiredSize = 0; @@ -2281,7 +2281,7 @@ namespace Avalonia.Controls /// Start of the range. /// Number of items in the range. /// Final size. - private double GetFinalSizeForRange( + private static double GetFinalSizeForRange( IReadOnlyList definitions, int start, int count) diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index 1a4736fa92..db8f038177 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -288,13 +288,13 @@ namespace Avalonia.Controls _resizeData.Definition1 = GetGridDefinition(_resizeData.Grid, index1, _resizeData.ResizeDirection); _resizeData.OriginalDefinition1Length = _resizeData.Definition1.UserSizeValueCache; // Save Size if user cancels. - _resizeData.OriginalDefinition1ActualLength = GridSplitter.GetActualLength(_resizeData.Definition1); + _resizeData.OriginalDefinition1ActualLength = GetActualLength(_resizeData.Definition1); _resizeData.Definition2Index = index2; _resizeData.Definition2 = GetGridDefinition(_resizeData.Grid, index2, _resizeData.ResizeDirection); _resizeData.OriginalDefinition2Length = _resizeData.Definition2.UserSizeValueCache; // Save Size if user cancels. - _resizeData.OriginalDefinition2ActualLength = GridSplitter.GetActualLength(_resizeData.Definition2); + _resizeData.OriginalDefinition2ActualLength = GetActualLength(_resizeData.Definition2); // Determine how to resize the columns. bool isStar1 = IsStar(_resizeData.Definition1); @@ -537,11 +537,11 @@ namespace Avalonia.Controls /// private void GetDeltaConstraints(out double minDelta, out double maxDelta) { - double definition1Len = GridSplitter.GetActualLength(_resizeData!.Definition1!); + double definition1Len = GetActualLength(_resizeData!.Definition1!); double definition1Min = _resizeData.Definition1!.UserMinSizeValueCache; double definition1Max = _resizeData.Definition1.UserMaxSizeValueCache; - double definition2Len = GridSplitter.GetActualLength(_resizeData.Definition2!); + double definition2Len = GetActualLength(_resizeData.Definition2!); double definition2Min = _resizeData.Definition2!.UserMinSizeValueCache; double definition2Max = _resizeData.Definition2.UserMaxSizeValueCache; @@ -590,7 +590,7 @@ namespace Avalonia.Controls } else if (IsStar(definition)) { - SetDefinitionLength(definition, new GridLength(GridSplitter.GetActualLength(definition), GridUnitType.Star)); + SetDefinitionLength(definition, new GridLength(GetActualLength(definition), GridUnitType.Star)); } } } @@ -629,8 +629,8 @@ namespace Avalonia.Controls if (definition1 != null && definition2 != null) { - double actualLength1 = GridSplitter.GetActualLength(definition1); - double actualLength2 = GridSplitter.GetActualLength(definition2); + double actualLength1 = GetActualLength(definition1); + double actualLength2 = GetActualLength(definition2); double pixelLength = 1 / _resizeData.Scaling; double epsilon = pixelLength + LayoutHelper.LayoutEpsilon; diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs index e107d2c217..d0d4bcc8b4 100644 --- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs +++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs @@ -64,7 +64,7 @@ namespace Avalonia.Platform var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); tl?.PlatformImpl?.Input?.Invoke(rawEvent); - var effect = InProcessDragSource.GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); + var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers); UpdateCursor(root, effect); return effect; } @@ -80,7 +80,7 @@ namespace Avalonia.Platform return DragDropEffects.Move; } - private StandardCursorType GetCursorForDropEffect(DragDropEffects effects) + private static StandardCursorType GetCursorForDropEffect(DragDropEffects effects) { if (effects.HasAllFlags(DragDropEffects.Copy)) return StandardCursorType.DragCopy; diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index e5bf924120..2a35fd2a12 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -295,7 +295,7 @@ namespace Avalonia.Controls.Presenters // arrange then that change wasn't just due to scrolling (as scrolling doesn't adjust // relative positions within Child). if (_anchorElement != null && - ScrollContentPresenter.TranslateBounds(_anchorElement, Child!, out var updatedBounds) && + TranslateBounds(_anchorElement, Child!, out var updatedBounds) && updatedBounds.Position != _anchorElementBounds.Position) { var offset = updatedBounds.Position - _anchorElementBounds.Position; @@ -588,7 +588,7 @@ namespace Avalonia.Controls.Presenters private bool GetViewportBounds(IControl element, out Rect bounds) { - if (ScrollContentPresenter.TranslateBounds(element, Child!, out var childBounds)) + if (TranslateBounds(element, Child!, out var childBounds)) { // We want the bounds relative to the new Offset, regardless of whether the child // control has actually been arranged to this offset yet, so translate first to the @@ -603,9 +603,9 @@ namespace Avalonia.Controls.Presenters return false; } - private Rect TranslateBounds(IControl control, IControl to) + private static Rect TranslateBounds(IControl control, IControl to) { - if (ScrollContentPresenter.TranslateBounds(control, to, out var bounds)) + if (TranslateBounds(control, to, out var bounds)) { return bounds; } diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index 12a17b8c9f..b681b43ce3 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -139,7 +139,7 @@ namespace Avalonia.Controls.Primitives private static void Attach(Visual visual, Control adorner) { - var layer = AdornerLayer.GetAdornerLayer(visual); + var layer = GetAdornerLayer(visual); AddVisualAdorner(visual, adorner, layer); visual.SetValue(s_savedAdornerLayerProperty, layer); } @@ -158,8 +158,8 @@ namespace Avalonia.Controls.Primitives return; } - AdornerLayer.SetAdornedElement(adorner, visual); - AdornerLayer.SetIsClipEnabled(adorner, false); + SetAdornedElement(adorner, visual); + SetIsClipEnabled(adorner, false); ((ISetLogicalParent) adorner).SetParent(visual); layer.Children.Add(adorner); @@ -211,7 +211,7 @@ namespace Avalonia.Controls.Primitives { child.RenderTransform = new MatrixTransform(info.Bounds.Value.Transform); child.RenderTransformOrigin = new RelativePoint(new Point(0, 0), RelativeUnit.Absolute); - AdornerLayer.UpdateClip(child, info.Bounds.Value, isClipEnabled); + UpdateClip(child, info.Bounds.Value, isClipEnabled); child.Arrange(info.Bounds.Value.Bounds); } else diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 581a65f6b9..4fc58d5462 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using System.Linq; using System.Reactive.Disposables; using Avalonia.Automation.Peers; using Avalonia.Controls.Mixins; @@ -15,7 +14,6 @@ using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.VisualTree; using Avalonia.Media; -using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -475,7 +473,7 @@ namespace Avalonia.Controls.Primitives _openState = new PopupOpenState(placementTarget, topLevel, popupHost, cleanupPopup); - Popup.WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); + WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); popupHost.Show(); @@ -769,7 +767,7 @@ namespace Avalonia.Controls.Primitives } } - private void PassThroughEvent(PointerPressedEventArgs e) + private static void PassThroughEvent(PointerPressedEventArgs e) { if (e.Source is LightDismissOverlayLayer layer && layer.GetVisualRoot() is IInputElement root) diff --git a/src/Avalonia.Controls/Repeater/RecyclePool.cs b/src/Avalonia.Controls/Repeater/RecyclePool.cs index 9d6ad37721..0c19a883df 100644 --- a/src/Avalonia.Controls/Repeater/RecyclePool.cs +++ b/src/Avalonia.Controls/Repeater/RecyclePool.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls public void PutElement(IControl element, string key, IControl? owner) { - var ownerAsPanel = RecyclePool.EnsureOwnerIsPanelOrNull(owner); + var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner); var elementInfo = new ElementInfo(element, ownerAsPanel); if (!_elements.TryGetValue(key, out var pool)) @@ -56,7 +56,7 @@ namespace Avalonia.Controls var elementInfo = elements.FirstOrDefault(x => x.Owner == owner) ?? elements.LastOrDefault(); elements.Remove(elementInfo!); - var ownerAsPanel = RecyclePool.EnsureOwnerIsPanelOrNull(owner); + var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner); if (elementInfo!.Owner != null && elementInfo.Owner != ownerAsPanel) { // Element is still under its parent. remove it from its parent. diff --git a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs index 1258c58324..7d09155ea4 100644 --- a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs +++ b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs @@ -72,7 +72,7 @@ namespace Avalonia.Controls element = dataTemplate.Build(args.Data)!; // Associate ReuseKey with element - Avalonia.Controls.RecyclePool.SetReuseKey(element, templateKey); + RecyclePool.SetReuseKey(element, templateKey); } return element; @@ -81,13 +81,13 @@ namespace Avalonia.Controls protected override void RecycleElementCore(ElementFactoryRecycleArgs args) { var element = args.Element!; - var key = Avalonia.Controls.RecyclePool.GetReuseKey(element); + var key = RecyclePool.GetReuseKey(element); RecyclePool.PutElement(element, key, args.Parent); } protected virtual string OnSelectTemplateKeyCore(object? dataContext, IControl? owner) { - if (SelectTemplateKey is object) + if (SelectTemplateKey is not null) { _args ??= new SelectTemplateEventArgs(); _args.TemplateKey = null; diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs index 2d735a2cbb..d4293446d9 100644 --- a/src/Avalonia.Controls/SplitView.cs +++ b/src/Avalonia.Controls/SplitView.cs @@ -1,14 +1,11 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Input; -using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Metadata; -using Avalonia.Platform; using Avalonia.VisualTree; using System; -using System.Reactive.Disposables; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.LogicalTree; @@ -443,7 +440,7 @@ namespace Avalonia.Controls }; } - private string GetPseudoClass(SplitViewPanePlacement placement) + private static string GetPseudoClass(SplitViewPanePlacement placement) { return placement switch { @@ -463,8 +460,8 @@ namespace Avalonia.Controls private void OnDisplayModeChanged(AvaloniaPropertyChangedEventArgs e) { - var oldState = SplitView.GetPseudoClass(e.GetOldValue()); - var newState = SplitView.GetPseudoClass(e.GetNewValue()); + var oldState = GetPseudoClass(e.GetOldValue()); + var newState = GetPseudoClass(e.GetNewValue()); PseudoClasses.Remove($":{oldState}"); PseudoClasses.Add($":{newState}"); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 0ac57c9233..331c75cd1f 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -17,7 +17,6 @@ using Avalonia.Controls.Metadata; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Automation.Peers; -using System.Diagnostics; using Avalonia.Threading; namespace Avalonia.Controls @@ -397,9 +396,9 @@ namespace Avalonia.Controls var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; - CaretIndex = TextBox.CoerceCaretIndex(caretIndex, value); - SelectionStart = TextBox.CoerceCaretIndex(selectionStart, value); - SelectionEnd = TextBox.CoerceCaretIndex(selectionEnd, value); + CaretIndex = CoerceCaretIndex(caretIndex, value); + SelectionStart = CoerceCaretIndex(selectionStart, value); + SelectionEnd = CoerceCaretIndex(selectionEnd, value); var textChanged = SetAndRaise(TextProperty, ref _text, value); @@ -1380,7 +1379,7 @@ namespace Avalonia.Controls } } - private int CoerceCaretIndex(int value) => TextBox.CoerceCaretIndex(value, Text); + private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text); private static int CoerceCaretIndex(int value, string? text) { diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 9fad9824df..b723d21710 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -449,7 +449,7 @@ namespace Avalonia.Controls { if(transparencyLevel == WindowTransparencyLevel.None || TransparencyLevelHint == WindowTransparencyLevel.None || - !TopLevel.TransparencyLevelsMatch(TransparencyLevelHint, transparencyLevel)) + !TransparencyLevelsMatch(TransparencyLevelHint, transparencyLevel)) { _transparencyFallbackBorder.Background = TransparencyBackgroundFallback; } diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index be30792cc8..e897339afd 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -3,13 +3,11 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; -using System.ComponentModel; using System.Linq; using System.Reactive.Linq; using Avalonia.Collections; using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; -using Avalonia.Controls.Utils; using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; @@ -284,7 +282,7 @@ namespace Avalonia.Controls foreach (IControl container in ItemContainerGenerator.Index!.Containers) { - TreeView.MarkContainerSelected(container, false); + MarkContainerSelected(container, false); } if (SelectedItems.Count > 0) @@ -339,7 +337,7 @@ namespace Avalonia.Controls { var container = ItemContainerGenerator.Index!.ContainerFromItem(item)!; - TreeView.MarkContainerSelected(container, selected); + MarkContainerSelected(container, selected); } private void SelectedItemsAdded(IList items) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs b/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs index 325c55783c..dca1e8008c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs @@ -61,7 +61,7 @@ namespace Avalonia.Diagnostics.Screenshots protected async override Task GetStream(IControl control) { Stream? output = default; - var result = await FilePickerHandler.GetWindow(control).StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + var result = await GetWindow(control).StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { SuggestedStartLocation = new BclStorageFolder(new DirectoryInfo(ScreenshotsRoot)), Title = Title, diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 869b2fd600..39b9e3d3f0 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -34,8 +34,8 @@ namespace Avalonia.Diagnostics.ViewModels { _avaloniaObject = avaloniaObject; - TreePage = treePage; - Layout = avaloniaObject is IVisual + TreePage = treePage; + Layout = avaloniaObject is IVisual ? new ControlLayoutViewModel((IVisual)avaloniaObject) : default; @@ -83,7 +83,7 @@ namespace Avalonia.Diagnostics.ViewModels { var setterValue = regularSetter.Value; - var resourceInfo = ControlDetailsViewModel.GetResourceInfo(setterValue); + var resourceInfo = GetResourceInfo(setterValue); SetterViewModel setterVm; @@ -137,7 +137,7 @@ namespace Avalonia.Diagnostics.ViewModels return null; } - private bool IsBinding(object? value) + private static bool IsBinding(object? value) { switch (value) { @@ -254,7 +254,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private IEnumerable GetAvaloniaProperties(object o) + private static IEnumerable GetAvaloniaProperties(object o) { if (o is AvaloniaObject ao) { @@ -268,7 +268,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private IEnumerable GetClrProperties(object o, bool showImplementedInterfaces) + private static IEnumerable GetClrProperties(object o, bool showImplementedInterfaces) { foreach (var p in GetClrProperties(o, o.GetType())) { @@ -287,7 +287,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private IEnumerable GetClrProperties(object o, Type t) + private static IEnumerable GetClrProperties(object o, Type t) { return t.GetProperties() .Where(x => x.GetIndexParameters().Length == 0) @@ -412,7 +412,7 @@ namespace Avalonia.Diagnostics.ViewModels } } - private int GroupIndex(string? group) + private static int GroupIndex(string? group) { switch (group) { diff --git a/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs b/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs index 2d6a2569fb..34c1506a67 100644 --- a/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs +++ b/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs @@ -36,12 +36,12 @@ namespace Avalonia.FreeDesktop private static string GetSymlinkTarget(string x) => Path.GetFullPath(Path.Combine(DevByLabelDir, NativeMethods.ReadLink(x))); - private string UnescapeString(string input, string regexText, int escapeBase) => + private static string UnescapeString(string input, string regexText, int escapeBase) => new Regex(regexText).Replace(input, m => Convert.ToChar(Convert.ToByte(m.Groups[1].Value, escapeBase)).ToString()); - private string UnescapePathFromProcMounts(string input) => UnescapeString(input, @"\\(\d{3})", 8); + private static string UnescapePathFromProcMounts(string input) => UnescapeString(input, @"\\(\d{3})", 8); - private string UnescapeDeviceLabel(string input) => UnescapeString(input, @"\\x([0-9a-f]{2})", 16); + private static string UnescapeDeviceLabel(string input) => UnescapeString(input, @"\\x([0-9a-f]{2})", 16); private void Poll(long _) { @@ -61,7 +61,7 @@ namespace Avalonia.FreeDesktop new DirectoryInfo(DevByLabelDir).GetFiles() : Enumerable.Empty(); var labelDevPathPairs = labelDirEnum - .Select(x => (LinuxMountedVolumeInfoListener.GetSymlinkTarget(x.FullName), UnescapeDeviceLabel(x.Name))); + .Select(x => (GetSymlinkTarget(x.FullName), UnescapeDeviceLabel(x.Name))); var q1 = from mount in fProcMounts join device in fProcPartitions on mount.Item1 equals device.Item2 diff --git a/src/Avalonia.Native/AvaloniaNativeDragSource.cs b/src/Avalonia.Native/AvaloniaNativeDragSource.cs index f93d558d25..84e2b31f23 100644 --- a/src/Avalonia.Native/AvaloniaNativeDragSource.cs +++ b/src/Avalonia.Native/AvaloniaNativeDragSource.cs @@ -48,7 +48,7 @@ namespace Avalonia.Native public Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects) { // Sanity check - var tl = AvaloniaNativeDragSource.FindRoot(triggerEvent.Source); + var tl = FindRoot(triggerEvent.Source); var view = tl?.PlatformImpl as WindowBaseImpl; if (view == null) throw new ArgumentException(); diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index ddabfe8f5d..7c7b32f7e4 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -167,7 +167,7 @@ namespace Avalonia.Native if (appMenu == null) { - appMenu = AvaloniaNativeMenuExporter.CreateDefaultAppMenu(); + appMenu = CreateDefaultAppMenu(); NativeMenu.SetMenu(Application.Current, appMenu); } diff --git a/src/Avalonia.Native/IAvnMenu.cs b/src/Avalonia.Native/IAvnMenu.cs index f75621865c..709980c6b9 100644 --- a/src/Avalonia.Native/IAvnMenu.cs +++ b/src/Avalonia.Native/IAvnMenu.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Reactive.Disposables; using Avalonia.Controls; -using Avalonia.Platform.Interop; namespace Avalonia.Native.Interop { @@ -111,7 +110,7 @@ namespace Avalonia.Native.Interop.Impl private __MicroComIAvnMenuItemProxy CreateNewAt(IAvaloniaNativeFactory factory, int index, NativeMenuItemBase item) { - var result = __MicroComIAvnMenuProxy.CreateNew(factory, item); + var result = CreateNew(factory, item); result.Initialize(item); diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs index 6f87ff19ee..e13ee80864 100644 --- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs @@ -27,7 +27,7 @@ namespace Avalonia.OpenGL.Controls _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb); EnsureTextureAttachment(); EnsureDepthBufferAttachment(_context.GlInterface); - if(!OpenGlControlBase.CheckFramebufferStatus(_context.GlInterface)) + if(!CheckFramebufferStatus(_context.GlInterface)) return; OnOpenGlRender(_context.GlInterface, _fb); @@ -186,7 +186,7 @@ namespace Avalonia.OpenGL.Controls EnsureDepthBufferAttachment(gl); EnsureTextureAttachment(); - return OpenGlControlBase.CheckFramebufferStatus(gl); + return CheckFramebufferStatus(gl); } catch(Exception e) { diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs index 94073506e3..ed55192bf5 100644 --- a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs +++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs @@ -26,8 +26,8 @@ namespace Avalonia.ReactiveUI public IObservable GetActivationForView(IActivatableView view) { if (!(view is IVisual visual)) return Observable.Return(false); - if (view is Control control) return AvaloniaActivationForViewFetcher.GetActivationForControl(control); - return AvaloniaActivationForViewFetcher.GetActivationForVisual(visual); + if (view is Control control) return GetActivationForControl(control); + return GetActivationForVisual(visual); } /// diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index de7c57a556..4ae1c1599f 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -16,9 +16,9 @@ namespace Avalonia.X11 return rv; } - public IWindowIconImpl LoadIcon(string fileName) => X11IconLoader.LoadIcon(new Bitmap(fileName)); + public IWindowIconImpl LoadIcon(string fileName) => LoadIcon(new Bitmap(fileName)); - public IWindowIconImpl LoadIcon(Stream stream) => X11IconLoader.LoadIcon(new Bitmap(stream)); + public IWindowIconImpl LoadIcon(Stream stream) => LoadIcon(new Bitmap(stream)); public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) { diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 312945e713..6ffebaf32b 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -5,7 +5,6 @@ using System.Reflection; using System.Runtime.InteropServices; using Avalonia.Controls; using Avalonia.Controls.Platform; -using Avalonia.Dialogs; using Avalonia.FreeDesktop; using Avalonia.FreeDesktop.DBusIme; using Avalonia.Input; @@ -42,10 +41,10 @@ namespace Avalonia.X11 Options = options; bool useXim = false; - if (AvaloniaX11Platform.EnableIme(options)) + if (EnableIme(options)) { // Attempt to configure DBus-based input method and check if we can fall back to XIM - if (!X11DBusImeHelper.DetectAndRegister() && AvaloniaX11Platform.ShouldUseXim()) + if (!X11DBusImeHelper.DetectAndRegister() && ShouldUseXim()) useXim = true; } @@ -85,7 +84,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new LinuxMountedVolumeInfoProvider()) .Bind().ToConstant(new X11PlatformLifetimeEvents(this)); - X11Screens = Avalonia.X11.X11Screens.Init(this); + X11Screens = X11.X11Screens.Init(this); Screens = new X11Screens(X11Screens); if (Info.XInputVersion != null) { diff --git a/src/Avalonia.X11/X11Window.Ime.cs b/src/Avalonia.X11/X11Window.Ime.cs index 128d48957c..8b267b2762 100644 --- a/src/Avalonia.X11/X11Window.Ime.cs +++ b/src/Avalonia.X11/X11Window.Ime.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; using Avalonia.FreeDesktop; using Avalonia.Input; using Avalonia.Input.Raw; @@ -107,7 +106,7 @@ namespace Avalonia.X11 var filtered = ScheduleKeyInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(), _inputRoot, ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - X11KeyTransform.ConvertKey(key), X11Window.TranslateModifiers(ev.KeyEvent.state)), ref ev, (int)key, ev.KeyEvent.keycode); + X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev, (int)key, ev.KeyEvent.keycode); if (ev.type == XEventName.KeyPress && !filtered) TriggerClassicTextInputEvent(ref ev); diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index b2120b718e..5d8ff0884b 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; -using System.Reactive.Disposables; using System.Text; using System.Threading.Tasks; using System.Threading; @@ -463,7 +461,7 @@ namespace Avalonia.X11 : new Vector(-1, 0); ScheduleInput(new RawMouseWheelEventArgs(_mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), delta, - X11Window.TranslateModifiers(ev.ButtonEvent.state)), ref ev); + TranslateModifiers(ev.ButtonEvent.state)), ref ev); } } @@ -760,7 +758,7 @@ namespace Avalonia.X11 { var mev = new RawPointerEventArgs( _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, - type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), X11Window.TranslateModifiers(mods)); + type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods)); ScheduleInput(mev, ref ev); } diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index 952112b6fd..cfe55036a3 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -193,7 +193,7 @@ namespace Avalonia.X11 { var rev = (XIEnterLeaveEvent*)xev; if (_clients.TryGetValue(rev->EventWindow, out var client)) - XI2Manager.OnEnterLeaveEvent(client, ref *rev); + OnEnterLeaveEvent(client, ref *rev); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs index 12406df765..264361e743 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Xml; using Avalonia.Markup.Xaml.Parsers; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; using Avalonia.Utilities; -using XamlX; using XamlX.Ast; using XamlX.Transform; using XamlX.Transform.Transformers; diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs index 078efcab6e..24b690b6f1 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs @@ -20,7 +20,7 @@ namespace Avalonia.Markup.Xaml.Converters var path = value as string; if (path != null) { - return IconTypeConverter.CreateIconFromPath(context, path); + return CreateIconFromPath(context, path); } var bitmap = value as IBitmap; diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 01d98ac9fb..dbcac7a2a3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -56,12 +56,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (control != null) { - var source = control.GetResourceObservable(ResourceKey, DynamicResourceExtension.GetConverter(targetProperty)); + var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); return InstancedBinding.OneWay(source, _priority); } else if (_anchor is IResourceProvider resourceProvider) { - var source = resourceProvider.GetResourceObservable(ResourceKey, DynamicResourceExtension.GetConverter(targetProperty)); + var source = resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); return InstancedBinding.OneWay(source, _priority); } diff --git a/src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs b/src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs index de463e2903..507e0690fd 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs @@ -1,6 +1,4 @@ -using System; -using Avalonia.Data.Core; -using Avalonia.Markup.Parsers; +using Avalonia.Data.Core; using Avalonia.Utilities; namespace Avalonia.Markup.Xaml.Parsers diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs index 824911902b..6176608196 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/Nodes/StringIndexerNode.cs @@ -113,9 +113,9 @@ namespace Avalonia.Markup.Parsers.Nodes } - private bool SetValueInArray(Array array, int[] indices, object? value) + private static bool SetValueInArray(Array array, int[] indices, object? value) { - if (StringIndexerNode.ValidBounds(indices, array)) + if (ValidBounds(indices, array)) { array.SetValue(value, indices); return true; @@ -223,9 +223,9 @@ namespace Avalonia.Markup.Parsers.Nodes return GetValueFromArray(array, intArgs); } - private object? GetValueFromArray(Array array, int[] indices) + private static object? GetValueFromArray(Array array, int[] indices) { - if (StringIndexerNode.ValidBounds(indices, array)) + if (ValidBounds(indices, array)) { return array.GetValue(indices); } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 46c8109fd8..60be732b8e 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -429,7 +429,7 @@ namespace Avalonia.Skia var spread = (float)boxShadow.Spread; var offsetX = (float)boxShadow.OffsetX; var offsetY = (float)boxShadow.OffsetY; - var outerRect = DrawingContextImpl.AreaCastingShadowInHole(rc, (float)boxShadow.Blur, spread, offsetX, offsetY); + var outerRect = AreaCastingShadowInHole(rc, (float)boxShadow.Blur, spread, offsetX, offsetY); Canvas.Save(); using var shadowRect = new SKRoundRect(skRoundRect); @@ -1044,7 +1044,7 @@ namespace Avalonia.Skia if (brush is IGradientBrush gradient) { - DrawingContextImpl.ConfigureGradientBrush(ref paintWrapper, targetSize, gradient); + ConfigureGradientBrush(ref paintWrapper, targetSize, gradient); return paintWrapper; } diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs index 813903c270..85c97307e8 100644 --- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs +++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs @@ -31,7 +31,7 @@ namespace Avalonia.Win32 public async Task GetTextAsync() { - using(await ClipboardImpl.OpenClipboard()) + using(await OpenClipboard()) { IntPtr hText = UnmanagedMethods.GetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT); if (hText == IntPtr.Zero) @@ -58,7 +58,7 @@ namespace Avalonia.Win32 throw new ArgumentNullException(nameof(text)); } - using(await ClipboardImpl.OpenClipboard()) + using(await OpenClipboard()) { UnmanagedMethods.EmptyClipboard(); @@ -69,7 +69,7 @@ namespace Avalonia.Win32 public async Task ClearAsync() { - using(await ClipboardImpl.OpenClipboard()) + using(await OpenClipboard()) { UnmanagedMethods.EmptyClipboard(); } diff --git a/src/Windows/Avalonia.Win32/CursorFactory.cs b/src/Windows/Avalonia.Win32/CursorFactory.cs index 73e45db6d3..862e99be19 100644 --- a/src/Windows/Avalonia.Win32/CursorFactory.cs +++ b/src/Windows/Avalonia.Win32/CursorFactory.cs @@ -92,7 +92,7 @@ namespace Avalonia.Win32 public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) { - using var source = CursorFactory.LoadSystemDrawingBitmap(cursor); + using var source = LoadSystemDrawingBitmap(cursor); using var mask = AlphaToMask(source); var info = new UnmanagedMethods.ICONINFO diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index c6ac9e63f5..27560df35e 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -288,7 +288,7 @@ namespace Avalonia.Win32 var byteArr = bytes is byte[] ? (byte[])bytes : bytes.ToArray(); return WriteBytesToHGlobal(ref hGlobal, byteArr); } - return WriteBytesToHGlobal(ref hGlobal, DataObject.SerializeObject(data)); + return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data)); } private static byte[] SerializeObject(object data) @@ -302,7 +302,7 @@ namespace Avalonia.Win32 } } - private unsafe uint WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan data) + private static unsafe uint WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan data) { int required = data.Length; if (hGlobal == IntPtr.Zero) @@ -326,7 +326,7 @@ namespace Avalonia.Win32 } } - private uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable files) + private static uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable files) { if (!files?.Any() ?? false) return unchecked((int)UnmanagedMethods.HRESULT.S_OK); @@ -358,7 +358,7 @@ namespace Avalonia.Win32 } } - private uint WriteStringToHGlobal(ref IntPtr hGlobal, string data) + private static uint WriteStringToHGlobal(ref IntPtr hGlobal, string data) { int required = (data.Length + 1) * sizeof(char); if (hGlobal == IntPtr.Zero) diff --git a/src/tools/DevGenerators/CompositionGenerator/Generator.cs b/src/tools/DevGenerators/CompositionGenerator/Generator.cs index c5f5ffcea4..0fb0389ca8 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Generator.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Generator.cs @@ -540,7 +540,7 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); return cl.AddMembers(method); } - ClassDeclarationSyntax WithStartAnimation(ClassDeclarationSyntax cl, BlockSyntax body) + static ClassDeclarationSyntax WithStartAnimation(ClassDeclarationSyntax cl, BlockSyntax body) { body = body.AddStatements( ExpressionStatement(InvocationExpression(MemberAccess("base", "StartAnimation"), From 6213676115ae823cb70b1f8ad674a3900836a87b Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 17 Oct 2022 17:00:15 +0200 Subject: [PATCH 022/173] fix: Address CA1822 rule --- src/Windows/Avalonia.Win32/Win32Platform.cs | 38 +++++++++---------- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 4 +- .../WindowImpl.CustomCaptionProc.cs | 4 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 7 ++-- .../CompositionGenerator/Generator.cs | 36 +++++++++--------- .../RenderTests_Culling.cs | 2 +- .../Rendering/DeferredRendererTests.cs | 10 ++--- .../AutoCompleteBoxTests.cs | 2 +- .../ButtonTests.cs | 2 +- .../CalendarDatePickerTests.cs | 4 +- .../Avalonia.Controls.UnitTests/GridTests.cs | 4 +- .../ListBoxTests.cs | 6 +-- .../MaskedTextBoxTests.cs | 6 +-- .../NumericUpDownTests.cs | 2 +- .../Primitives/PopupTests.cs | 2 +- .../TextBoxTests.cs | 8 ++-- .../TreeViewTests.cs | 2 +- .../CompiledBindingExtensionTests.cs | 10 ++--- .../DynamicResourceExtensionTests.cs | 20 +++++----- .../StaticResourceExtensionTests.cs | 12 +++--- .../Xaml/ResourceDictionaryTests.cs | 26 ++++++------- .../Xaml/XamlIlTests.cs | 2 +- 22 files changed, 105 insertions(+), 104 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 3cdc3586dc..69b47540a9 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -131,10 +131,10 @@ namespace Avalonia.Win32 internal static Compositor Compositor { get; private set; } public Size DoubleClickSize => new Size( - UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CXDOUBLECLK), - UnmanagedMethods.GetSystemMetrics(UnmanagedMethods.SystemMetric.SM_CYDOUBLECLK)); + GetSystemMetrics(SystemMetric.SM_CXDOUBLECLK), + GetSystemMetrics(SystemMetric.SM_CYDOUBLECLK)); - public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(UnmanagedMethods.GetDoubleClickTime()); + public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(GetDoubleClickTime()); /// public Size TouchDoubleClickSize => new Size(16,16); @@ -185,16 +185,16 @@ namespace Avalonia.Win32 public bool HasMessages() { UnmanagedMethods.MSG msg; - return UnmanagedMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); + return PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); } public void ProcessMessage() { - if (UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1) + if (GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1) { - UnmanagedMethods.TranslateMessage(ref msg); - UnmanagedMethods.DispatchMessage(ref msg); + TranslateMessage(ref msg); + DispatchMessage(ref msg); } else { @@ -208,10 +208,10 @@ namespace Avalonia.Win32 { var result = 0; while (!cancellationToken.IsCancellationRequested - && (result = UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0)) > 0) + && (result = GetMessage(out var msg, IntPtr.Zero, 0, 0)) > 0) { - UnmanagedMethods.TranslateMessage(ref msg); - UnmanagedMethods.DispatchMessage(ref msg); + TranslateMessage(ref msg); + DispatchMessage(ref msg); } if (result < 0) { @@ -225,7 +225,7 @@ namespace Avalonia.Win32 UnmanagedMethods.TimerProc timerDelegate = (hWnd, uMsg, nIDEvent, dwTime) => callback(); - IntPtr handle = UnmanagedMethods.SetTimer( + IntPtr handle = SetTimer( IntPtr.Zero, IntPtr.Zero, (uint)interval.TotalMilliseconds, @@ -237,7 +237,7 @@ namespace Avalonia.Win32 return Disposable.Create(() => { _delegates.Remove(timerDelegate); - UnmanagedMethods.KillTimer(IntPtr.Zero, handle); + KillTimer(IntPtr.Zero, handle); }); } @@ -246,9 +246,9 @@ namespace Avalonia.Win32 public void Signal(DispatcherPriority prio) { - UnmanagedMethods.PostMessage( + PostMessage( _hwnd, - (int) UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM, + (int)WindowsMessage.WM_DISPATCH_WORK_ITEM, new IntPtr(SignalW), new IntPtr(SignalL)); } @@ -262,7 +262,7 @@ namespace Avalonia.Win32 [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { - if (msg == (int) UnmanagedMethods.WindowsMessage.WM_DISPATCH_WORK_ITEM && wParam.ToInt64() == SignalW && lParam.ToInt64() == SignalL) + if (msg == (int)WindowsMessage.WM_DISPATCH_WORK_ITEM && wParam.ToInt64() == SignalW && lParam.ToInt64() == SignalL) { Signaled?.Invoke(null); } @@ -284,7 +284,7 @@ namespace Avalonia.Win32 TrayIconImpl.ProcWnd(hWnd, msg, wParam, lParam); - return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam); + return DefWindowProc(hWnd, msg, wParam, lParam); } private void CreateMessageWindow() @@ -296,18 +296,18 @@ namespace Avalonia.Win32 { cbSize = Marshal.SizeOf(), lpfnWndProc = _wndProcDelegate, - hInstance = UnmanagedMethods.GetModuleHandle(null), + hInstance = GetModuleHandle(null), lpszClassName = "AvaloniaMessageWindow " + Guid.NewGuid(), }; - ushort atom = UnmanagedMethods.RegisterClassEx(ref wndClassEx); + ushort atom = RegisterClassEx(ref wndClassEx); if (atom == 0) { throw new Win32Exception(); } - _hwnd = UnmanagedMethods.CreateWindowEx(0, atom, null, 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + _hwnd = CreateWindowEx(0, atom, null, 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (_hwnd == IntPtr.Zero) { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index f8785371d9..9855435e9f 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -356,7 +356,7 @@ namespace Avalonia.Win32 RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down, }, - PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam)); + PointToClient(WindowImpl.PointFromLParam(lParam)), GetMouseModifiers(wParam)); break; } case WindowsMessage.WM_TOUCH: @@ -948,7 +948,7 @@ namespace Avalonia.Win32 return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / RenderScaling; } - private PixelPoint PointFromLParam(IntPtr lParam) + private static PixelPoint PointFromLParam(IntPtr lParam) { return new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index e864f32138..c7dc90ed58 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs @@ -13,7 +13,7 @@ namespace Avalonia.Win32 private HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { // Get the point coordinates for the hit test (screen space). - var ptMouse = PointFromLParam(lParam); + var ptMouse = WindowImpl.PointFromLParam(lParam); // Get the window rectangle. GetWindowRect(hWnd, out var rcWindow); @@ -105,7 +105,7 @@ namespace Avalonia.Win32 if (hittestResult == HitTestValues.HTCAPTION) { - var position = PointToClient(PointFromLParam(lParam)); + var position = PointToClient(WindowImpl.PointFromLParam(lParam)); if (_owner is Window window) { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 0f243fcf9f..6e849a5b9d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -639,7 +639,7 @@ namespace Avalonia.Win32 public Point PointToClient(PixelPoint point) { var p = new POINT { X = point.X, Y = point.Y }; - UnmanagedMethods.ScreenToClient(_hwnd, ref p); + ScreenToClient(_hwnd, ref p); return new Point(p.X, p.Y) / RenderScaling; } @@ -1321,12 +1321,13 @@ namespace Avalonia.Win32 private const int MF_DISABLED = 0x2; private const int SC_CLOSE = 0xF060; - void DisableCloseButton(IntPtr hwnd) + static void DisableCloseButton(IntPtr hwnd) { EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } - void EnableCloseButton(IntPtr hwnd) + + static void EnableCloseButton(IntPtr hwnd) { EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, MF_BYCOMMAND | MF_ENABLED); diff --git a/src/tools/DevGenerators/CompositionGenerator/Generator.cs b/src/tools/DevGenerators/CompositionGenerator/Generator.cs index 0fb0389ca8..f8d5ad826e 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Generator.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Generator.cs @@ -311,7 +311,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator "Server", "Server" + cl.Name + ".generated.cs"); } - private ClassDeclarationSyntax GenerateClientProperty(ClassDeclarationSyntax client, GClass cl, GProperty prop, + private static ClassDeclarationSyntax GenerateClientProperty(ClassDeclarationSyntax client, GClass cl, GProperty prop, TypeSyntax propType, bool isObject, bool isNullable) { var fieldName = PropertyBackingFieldName(prop); @@ -344,7 +344,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator .AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon())); } - EnumDeclarationSyntax GenerateChangedFieldsEnum(GClass cl) + static EnumDeclarationSyntax GenerateChangedFieldsEnum(GClass cl) { var changedFieldsEnum = EnumDeclaration(Identifier(ChangedFieldsTypeName(cl))); int count = 0; @@ -371,7 +371,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator .AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("System.Flags"))))); } - StatementSyntax GeneratePropertySetterAssignment(GClass cl, GProperty prop, bool isObject, bool isNullable) + static StatementSyntax GeneratePropertySetterAssignment(GClass cl, GProperty prop, bool isObject, bool isNullable) { var code = @$" // Update the backing value @@ -404,8 +404,8 @@ namespace Avalonia.SourceGenerator.CompositionGenerator return ParseStatement("{\n" + code + "\n}"); } - - BlockSyntax ApplyStartAnimation(BlockSyntax body, GClass cl, GProperty prop) + + static BlockSyntax ApplyStartAnimation(BlockSyntax body, GClass cl, GProperty prop) { var code = $@" if (propertyName == ""{prop.Name}"") @@ -435,8 +435,8 @@ return; "Color", "Avalonia.Media.Color" }; - - BlockSyntax ApplyGetProperty(BlockSyntax body, GProperty prop, string? expr = null) + + static BlockSyntax ApplyGetProperty(BlockSyntax body, GProperty prop, string? expr = null) { if (VariantPropertyTypes.Contains(prop.Type)) return body.AddStatements( @@ -446,7 +446,7 @@ return; return body; } - private BlockSyntax SerializeChangesPrologue(GClass cl) + private static BlockSyntax SerializeChangesPrologue(GClass cl) { return Block( ParseStatement("base.SerializeChangesCore(writer);"), @@ -454,10 +454,10 @@ return; ); } - private BlockSyntax SerializeChangesEpilogue(GClass cl) => + private static BlockSyntax SerializeChangesEpilogue(GClass cl) => Block(ParseStatement(ChangedFieldsFieldName(cl) + " = default;")); - - BlockSyntax ApplySerializeField(BlockSyntax body, GClass cl, GProperty prop, bool isObject, bool isPassthrough) + + static BlockSyntax ApplySerializeField(BlockSyntax body, GClass cl, GProperty prop, bool isObject, bool isPassthrough) { var changedFields = ChangedFieldsFieldName(cl); var changedFieldsType = ChangedFieldsTypeName(cl); @@ -478,7 +478,7 @@ return; return body.AddStatements(ParseStatement(code)); } - private BlockSyntax DeserializeChangesPrologue(GClass cl) + private static BlockSyntax DeserializeChangesPrologue(GClass cl) { return Block(ParseStatement($@" base.DeserializeChangesCore(reader, commitedAt); @@ -487,12 +487,12 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); ")); } - private BlockSyntax ApplyDeserializeChangesEpilogue(BlockSyntax body, GClass cl) + private static BlockSyntax ApplyDeserializeChangesEpilogue(BlockSyntax body, GClass cl) { return body.AddStatements(ParseStatement("OnFieldsDeserialized(changed);")); } - - BlockSyntax ApplyDeserializeField(BlockSyntax body, GClass cl, GProperty prop, string serverType, bool isObject) + + static BlockSyntax ApplyDeserializeField(BlockSyntax body, GClass cl, GProperty prop, string serverType, bool isObject) { var changedFieldsType = ChangedFieldsTypeName(cl); var code = ""; @@ -514,7 +514,7 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); return body.AddStatements(ParseStatement(code)); } - ClassDeclarationSyntax WithGetPropertyForAnimation(ClassDeclarationSyntax cl, BlockSyntax body) + static ClassDeclarationSyntax WithGetPropertyForAnimation(ClassDeclarationSyntax cl, BlockSyntax body) { if (body.Statements.Count == 0) return cl; @@ -526,8 +526,8 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); return cl.AddMembers(method); } - - ClassDeclarationSyntax WithGetCompositionProperty(ClassDeclarationSyntax cl, BlockSyntax body) + + static ClassDeclarationSyntax WithGetCompositionProperty(ClassDeclarationSyntax cl, BlockSyntax body) { if (body.Statements.Count == 0) return cl; diff --git a/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs b/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs index 509d2451a9..d17302808d 100644 --- a/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs +++ b/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs @@ -171,7 +171,7 @@ namespace Avalonia.Base.UnitTests } } - private void Render(IControl control) + private static void Render(IControl control) { var ctx = CreateDrawingContext(); control.Measure(Size.Infinity); diff --git a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs index 7f977103ce..df44575f68 100644 --- a/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs @@ -726,7 +726,7 @@ namespace Avalonia.Base.UnitTests.Rendering } } - private DeferredRenderer CreateTargetAndRunFrame( + private static DeferredRenderer CreateTargetAndRunFrame( TestRoot root, Mock timer = null, ISceneBuilder sceneBuilder = null, @@ -750,25 +750,25 @@ namespace Avalonia.Base.UnitTests.Rendering return Mock.Get(renderer.Layers[layerRoot].Bitmap.Item.CreateDrawingContext(null)); } - private void IgnoreFirstFrame(IRenderLoopTask task, Mock sceneBuilder) + private static void IgnoreFirstFrame(IRenderLoopTask task, Mock sceneBuilder) { RunFrame(task); sceneBuilder.Invocations.Clear(); } - private void RunFrame(IRenderLoopTask task) + private static void RunFrame(IRenderLoopTask task) { task.Update(TimeSpan.Zero); task.Render(); } - private IRenderTargetBitmapImpl CreateLayer() + private static IRenderTargetBitmapImpl CreateLayer() { return Mock.Of(x => x.CreateDrawingContext(It.IsAny()) == Mock.Of()); } - private Mock MockSceneBuilder(IRenderRoot root) + private static Mock MockSceneBuilder(IRenderRoot root) { var result = new Mock(); result.Setup(x => x.UpdateAll(It.IsAny())) diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs index c8bd289e54..f97b69c752 100644 --- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs @@ -455,7 +455,7 @@ namespace Avalonia.Controls.UnitTests /// Creates a large list of strings for AutoCompleteBox testing. /// /// Returns a new List of string values. - private IList CreateSimpleStringArray() + private static IList CreateSimpleStringArray() { return new List { diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index 42bdffd908..68f46824c1 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -379,7 +379,7 @@ namespace Avalonia.Controls.UnitTests } } - private KeyEventArgs CreateKeyDownEvent(Key key, IInteractive source = null) + private static KeyEventArgs CreateKeyDownEvent(Key key, IInteractive source = null) { return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source }; } diff --git a/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs index a73e14939d..c3acf31caa 100644 --- a/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls.UnitTests private static TestServices Services => TestServices.MockThreadingInterface.With( standardCursorFactory: Mock.Of()); - private CalendarDatePicker CreateControl() + private static CalendarDatePicker CreateControl() { var datePicker = new CalendarDatePicker @@ -88,7 +88,7 @@ namespace Avalonia.Controls.UnitTests return datePicker; } - private IControlTemplate CreateTemplate() + private static IControlTemplate CreateTemplate() { return new FuncControlTemplate((control, scope) => { diff --git a/tests/Avalonia.Controls.UnitTests/GridTests.cs b/tests/Avalonia.Controls.UnitTests/GridTests.cs index b2d00929d8..d372bb2af4 100644 --- a/tests/Avalonia.Controls.UnitTests/GridTests.cs +++ b/tests/Avalonia.Controls.UnitTests/GridTests.cs @@ -16,13 +16,13 @@ namespace Avalonia.Controls.UnitTests this.output = output; } - private Grid CreateGrid(params (string name, GridLength width)[] columns) + private static Grid CreateGrid(params (string name, GridLength width)[] columns) { return CreateGrid(columns.Select(c => (c.name, c.width, ColumnDefinition.MinWidthProperty.GetDefaultValue(typeof(ColumnDefinition)))).ToArray()); } - private Grid CreateGrid(params (string name, GridLength width, double minWidth)[] columns) + private static Grid CreateGrid(params (string name, GridLength width, double minWidth)[] columns) { return CreateGrid(columns.Select(c => (c.name, c.width, c.minWidth, ColumnDefinition.MaxWidthProperty.GetDefaultValue(typeof(ColumnDefinition)))).ToArray()); diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 203df0aded..e65a3b62ee 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -590,7 +590,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new[] { "Bar" }, target.Selection.SelectedItems); } - private FuncControlTemplate ListBoxTemplate() + private static FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate((parent, scope) => new ScrollViewer @@ -643,7 +643,7 @@ namespace Avalonia.Controls.UnitTests }); } - private void Prepare(ListBox target) + private static void Prepare(ListBox target) { // The ListBox needs to be part of a rooted visual tree. var root = new TestRoot(); @@ -718,7 +718,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception })); } - private void RaiseKeyEvent(ListBox listBox, Key key, KeyModifiers inputModifiers = 0) + private static void RaiseKeyEvent(ListBox listBox, Key key, KeyModifiers inputModifiers = 0) { listBox.RaiseEvent(new KeyEventArgs { diff --git a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs index d1fa522206..22b4e0e87d 100644 --- a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs @@ -118,7 +118,7 @@ namespace Avalonia.Controls.UnitTests target.ApplyTemplate(); target.CaretIndex = 3; target.Measure(Size.Infinity); - + RaiseKeyEvent(target, Key.Right, 0); Assert.Equal(4, target.CaretIndex); @@ -885,7 +885,7 @@ namespace Avalonia.Controls.UnitTests textShaperImpl: new MockTextShaperImpl(), fontManagerImpl: new MockFontManagerImpl()); - private IControlTemplate CreateTemplate() + private static IControlTemplate CreateTemplate() { return new FuncControlTemplate((control, scope) => new TextPresenter @@ -908,7 +908,7 @@ namespace Avalonia.Controls.UnitTests }.RegisterInNameScope(scope)); } - private void RaiseKeyEvent(MaskedTextBox textBox, Key key, KeyModifiers inputModifiers) + private static void RaiseKeyEvent(MaskedTextBox textBox, Key key, KeyModifiers inputModifiers) { textBox.RaiseEvent(new KeyEventArgs { diff --git a/tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs b/tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs index dc47b19299..bdb94707de 100644 --- a/tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs +++ b/tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs @@ -75,7 +75,7 @@ namespace Avalonia.Controls.UnitTests .OfType() .First(); } - private IControlTemplate CreateTemplate() + private static IControlTemplate CreateTemplate() { return new FuncControlTemplate((control, scope) => { diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 100de7f4f6..68c9fba8b9 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -1074,7 +1074,7 @@ namespace Avalonia.Controls.UnitTests.Primitives } - private PointerPressedEventArgs CreatePointerPressedEventArgs(Window source, Point p) + private static PointerPressedEventArgs CreatePointerPressedEventArgs(Window source, Point p) { var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); return new PointerPressedEventArgs( diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 23a330c96f..d550d7c5f9 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -578,7 +578,7 @@ namespace Avalonia.Controls.UnitTests target1.Focus(); Assert.True(target1.IsFocused); - + RaiseKeyEvent(target1, key, KeyModifiers.None); } } @@ -747,7 +747,7 @@ namespace Avalonia.Controls.UnitTests var clipboard = AvaloniaLocator.CurrentMutable.GetService(); clipboard.SetTextAsync(textInput).GetAwaiter().GetResult(); - + RaiseKeyEvent(target, Key.V, KeyModifiers.Control); clipboard.ClearAsync().GetAwaiter().GetResult(); } @@ -904,7 +904,7 @@ namespace Avalonia.Controls.UnitTests }.RegisterInNameScope(scope)); } - private void RaiseKeyEvent(TextBox textBox, Key key, KeyModifiers inputModifiers) + private static void RaiseKeyEvent(TextBox textBox, Key key, KeyModifiers inputModifiers) { textBox.RaiseEvent(new KeyEventArgs { @@ -914,7 +914,7 @@ namespace Avalonia.Controls.UnitTests }); } - private void RaiseTextEvent(TextBox textBox, string text) + private static void RaiseTextEvent(TextBox textBox, string text) { textBox.RaiseEvent(new TextInputEventArgs { diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index fb21acad9e..81d76f8ca5 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -1247,7 +1247,7 @@ namespace Avalonia.Controls.UnitTests } } - private TreeViewItem GetItem(TreeView target, params int[] indexes) + private static TreeViewItem GetItem(TreeView target, params int[] indexes) { var c = (ItemsControl)target; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 215ae4d54f..418349e390 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1568,8 +1568,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(typeof(string), node.Property.PropertyType); } } - - void Throws(string type, Action cb) + + static void Throws(string type, Action cb) { try { @@ -1583,8 +1583,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions throw new Exception("Expected " + type); } - void ThrowsXamlParseException(Action cb) => Throws("XamlParseException", cb); - void ThrowsXamlTransformException(Action cb) => Throws("XamlTransformException", cb); + static void ThrowsXamlParseException(Action cb) => Throws("XamlParseException", cb); + static void ThrowsXamlTransformException(Action cb) => Throws("XamlTransformException", cb); static void PerformClick(Button button) @@ -1592,7 +1592,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions button.RaiseEvent(new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, - Key = Input.Key.Enter, + Key = Key.Enter, }); } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 4f323d8b2c..ccc460d900 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -221,7 +221,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions [Fact] public void DynamicResource_From_Style_Can_Be_Assigned_To_Setter() { - using (StyledWindow()) + using (DynamicResourceExtensionTests.StyledWindow()) { var xaml = @" "; - using (StyledWindow(assets: ("test:style.xaml", styleXaml))) + using (DynamicResourceExtensionTests.StyledWindow(assets: ("test:style.xaml", styleXaml))) { var xaml = @" "; - using (StyledWindow(assets: ("test:style.xaml", styleXaml))) + using (DynamicResourceExtensionTests.StyledWindow(assets: ("test:style.xaml", styleXaml))) { var xaml = @" "; - using (StyledWindow( + using (DynamicResourceExtensionTests.StyledWindow( ("test:style1.xaml", style1Xaml), ("test:style2.xaml", style2Xaml))) { @@ -606,7 +606,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - using (StyledWindow( + using (DynamicResourceExtensionTests.StyledWindow( ("test:style1.xaml", style1Xaml), ("test:style2.xaml", style2Xaml))) { @@ -631,7 +631,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions [Fact] public void Control_Property_Is_Updated_When_Parent_Is_Changed() { - using (StyledWindow()) + using (DynamicResourceExtensionTests.StyledWindow()) { var xaml = @" new Styles { - DynamicResourceExtensionTests.WindowStyle(), + WindowStyle(), }); return UnitTestApplication.Start(services); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index 625785a0b7..94e1e013d2 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -374,7 +374,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions [Fact] public void StaticResource_Can_Be_Assigned_To_Converter() { - using (StyledWindow()) + using (StaticResourceExtensionTests.StyledWindow()) { var xaml = @" new Styles { - StaticResourceExtensionTests.WindowStyle(), + WindowStyle(), }); return UnitTestApplication.Start(services); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs index 05e69adae0..030fa669b8 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs @@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml [Fact] public void StaticResource_Works_In_ResourceDictionary() { - using (StyledWindow()) + using (ResourceDictionaryTests.StyledWindow()) { var xaml = @" "; - using (StyledWindow(assets: ("test:dict.xaml", dictionaryXaml))) + using (ResourceDictionaryTests.StyledWindow(assets: ("test:dict.xaml", dictionaryXaml))) { var xaml = @" AvaloniaRuntimeXamlLoader + AssertThrows(() => AvaloniaRuntimeXamlLoader .Load(@" Date: Wed, 19 Oct 2022 09:58:58 +0200 Subject: [PATCH 023/173] fix: Apply rule CA1822 --- src/Avalonia.Base/Media/PathMarkupParser.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Media/PathMarkupParser.cs b/src/Avalonia.Base/Media/PathMarkupParser.cs index 30c5206125..41f97e5d1b 100644 --- a/src/Avalonia.Base/Media/PathMarkupParser.cs +++ b/src/Avalonia.Base/Media/PathMarkupParser.cs @@ -519,7 +519,7 @@ namespace Avalonia.Media return span.Slice(i); } - private bool ReadBool(ref ReadOnlySpan span) + private static bool ReadBool(ref ReadOnlySpan span) { span = SkipWhitespace(span); @@ -543,7 +543,7 @@ namespace Avalonia.Media } } - private double ReadDouble(ref ReadOnlySpan span) + private static double ReadDouble(ref ReadOnlySpan span) { if (!ReadArgument(ref span, out var doubleValue)) { @@ -553,7 +553,7 @@ namespace Avalonia.Media return double.Parse(doubleValue.ToString(), CultureInfo.InvariantCulture); } - private Size ReadSize(ref ReadOnlySpan span) + private static Size ReadSize(ref ReadOnlySpan span) { var width = ReadDouble(ref span); span = ReadSeparator(span); @@ -561,7 +561,7 @@ namespace Avalonia.Media return new Size(width, height); } - private Point ReadPoint(ref ReadOnlySpan span) + private static Point ReadPoint(ref ReadOnlySpan span) { var x = ReadDouble(ref span); span = ReadSeparator(span); @@ -569,7 +569,7 @@ namespace Avalonia.Media return new Point(x, y); } - private Point ReadRelativePoint(ref ReadOnlySpan span, Point origin) + private static Point ReadRelativePoint(ref ReadOnlySpan span, Point origin) { var x = ReadDouble(ref span); span = ReadSeparator(span); @@ -577,7 +577,7 @@ namespace Avalonia.Media return new Point(origin.X + x, origin.Y + y); } - private bool ReadCommand(ref ReadOnlySpan span, out Command command, out bool relative) + private static bool ReadCommand(ref ReadOnlySpan span, out Command command, out bool relative) { span = SkipWhitespace(span); if (span.IsEmpty) From ac2f0f88d6156278dc2de28c63a461a7aaed0d4e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 3 Nov 2022 14:48:39 +0100 Subject: [PATCH 024/173] fix: Rule CA1822 only private and internal --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index c3989db7f4..488453596d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -145,6 +145,7 @@ dotnet_diagnostic.CA1820.severity = warning dotnet_diagnostic.CA1821.severity = warning # CA1822: Mark members as static dotnet_diagnostic.CA1822.severity = warning +dotnet_code_quality.CA1822.api_surface = private, internal # CA1825: Avoid zero-length array allocations dotnet_diagnostic.CA1825.severity = warning #CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters From 581481f7adfe3d56899b04cee811b3d49756c0a0 Mon Sep 17 00:00:00 2001 From: zhouzj Date: Mon, 7 Nov 2022 09:52:06 +0800 Subject: [PATCH 025/173] Indicator size calculation should care about the ProgressBar's Padding property setting --- src/Avalonia.Controls/ProgressBar.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index 71a7a58da4..165bec3a95 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -270,15 +270,16 @@ namespace Avalonia.Controls double percent = Maximum == Minimum ? 1.0 : (Value - Minimum) / (Maximum - Minimum); // When the Orientation changed, the indicator's Width or Height should set to double.NaN. + // Indicator size calculation should consider the ProgressBar's Padding property setting if (Orientation == Orientation.Horizontal) { - _indicator.Width = barSize.Width * percent; + _indicator.Width = (barSize.Width - _indicator.Margin.Left - _indicator.Margin.Right) * percent; _indicator.Height = double.NaN; } else { _indicator.Width = double.NaN; - _indicator.Height = barSize.Height * percent; + _indicator.Height = (barSize.Height - _indicator.Margin.Top - _indicator.Margin.Bottom) * percent; } From 1e69527ea62b4f97c96fa1d0d2f7fc47d8db57dc Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 15 Nov 2022 10:12:48 +0100 Subject: [PATCH 026/173] fix: address review --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 488453596d..7c0301a124 100644 --- a/.editorconfig +++ b/.editorconfig @@ -144,7 +144,7 @@ dotnet_diagnostic.CA1820.severity = warning # CA1821: Remove empty finalizers dotnet_diagnostic.CA1821.severity = warning # CA1822: Mark members as static -dotnet_diagnostic.CA1822.severity = warning +dotnet_diagnostic.CA1822.severity = suggestion dotnet_code_quality.CA1822.api_surface = private, internal # CA1825: Avoid zero-length array allocations dotnet_diagnostic.CA1825.severity = warning From 5bdbd930d980999b249f3a7334b16fbd58b6e22e Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 16 Nov 2022 16:56:11 +0000 Subject: [PATCH 027/173] Add Refresh Container --- samples/ControlCatalog/MainView.xaml | 3 + .../Pages/RefreshContainerPage.axaml | 24 + .../Pages/RefreshContainerPage.axaml.cs | 36 ++ .../ViewModels/RefreshContainerViewModel.cs | 26 + src/Avalonia.Base/Input/Gestures.cs | 8 + .../Input/PullGestureEventArgs.cs | 43 ++ .../Input/PullGestureRecognizer.cs | 152 +++++ .../PullToRefresh/RefreshContainer.cs | 221 +++++++ .../PullToRefresh/RefreshInfoProvider.cs | 129 +++++ .../PullToRefresh/RefreshVisualizer.cs | 544 ++++++++++++++++++ ...ScrollViewerIRefreshInfoProviderAdapter.cs | 227 ++++++++ .../Accents/FluentControlResourcesDark.xaml | 6 +- .../Accents/FluentControlResourcesLight.xaml | 4 + .../Controls/FluentControls.xaml | 2 + .../Controls/RefreshContainer.xaml | 24 + .../Controls/RefreshVisualizer.xaml | 67 +++ .../Accents/BaseDark.xaml | 3 + .../Accents/BaseLight.xaml | 4 +- .../Controls/RefreshContainer.xaml | 24 + .../Controls/RefreshVisualizer.xaml | 72 +++ .../Controls/SimpleControls.xaml | 2 + 21 files changed, 1619 insertions(+), 2 deletions(-) create mode 100644 samples/ControlCatalog/Pages/RefreshContainerPage.axaml create mode 100644 samples/ControlCatalog/Pages/RefreshContainerPage.axaml.cs create mode 100644 samples/ControlCatalog/ViewModels/RefreshContainerViewModel.cs create mode 100644 src/Avalonia.Base/Input/PullGestureEventArgs.cs create mode 100644 src/Avalonia.Base/Input/PullGestureRecognizer.cs create mode 100644 src/Avalonia.Controls/PullToRefresh/RefreshContainer.cs create mode 100644 src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs create mode 100644 src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs create mode 100644 src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs create mode 100644 src/Avalonia.Themes.Fluent/Controls/RefreshContainer.xaml create mode 100644 src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml create mode 100644 src/Avalonia.Themes.Simple/Controls/RefreshContainer.xaml create mode 100644 src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index b5a09b5fbd..33ea8c0db0 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -132,6 +132,9 @@ + + + diff --git a/samples/ControlCatalog/Pages/RefreshContainerPage.axaml b/samples/ControlCatalog/Pages/RefreshContainerPage.axaml new file mode 100644 index 0000000000..0123251bd0 --- /dev/null +++ b/samples/ControlCatalog/Pages/RefreshContainerPage.axaml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/samples/ControlCatalog/Pages/RefreshContainerPage.axaml.cs b/samples/ControlCatalog/Pages/RefreshContainerPage.axaml.cs new file mode 100644 index 0000000000..4c52179e00 --- /dev/null +++ b/samples/ControlCatalog/Pages/RefreshContainerPage.axaml.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public class RefreshContainerPage : UserControl + { + private RefreshContainerViewModel _viewModel; + + public RefreshContainerPage() + { + this.InitializeComponent(); + + _viewModel = new RefreshContainerViewModel(); + + DataContext = _viewModel; + } + + private async void RefreshContainerPage_RefreshRequested(object? sender, RefreshRequestedEventArgs e) + { + var deferral = e.GetRefreshCompletionDeferral(); + + await _viewModel.AddToTop(); + + deferral.Complete(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/ControlCatalog/ViewModels/RefreshContainerViewModel.cs b/samples/ControlCatalog/ViewModels/RefreshContainerViewModel.cs new file mode 100644 index 0000000000..a78bc9e44a --- /dev/null +++ b/samples/ControlCatalog/ViewModels/RefreshContainerViewModel.cs @@ -0,0 +1,26 @@ +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive; +using System.Threading.Tasks; +using Avalonia.Controls.Notifications; +using ControlCatalog.Pages; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class RefreshContainerViewModel : ViewModelBase + { + public ObservableCollection Items { get; } + + public RefreshContainerViewModel() + { + Items = new ObservableCollection(Enumerable.Range(1, 200).Select(i => $"Item {i}")); + } + + public async Task AddToTop() + { + await Task.Delay(1000); + Items.Insert(0, $"Item {200 - Items.Count}"); + } + } +} diff --git a/src/Avalonia.Base/Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs index 82ed96d982..acd78515cd 100644 --- a/src/Avalonia.Base/Input/Gestures.cs +++ b/src/Avalonia.Base/Input/Gestures.cs @@ -46,6 +46,14 @@ namespace Avalonia.Input private static readonly WeakReference s_lastPress = new WeakReference(null); private static Point s_lastPressPoint; + public static readonly RoutedEvent PullGestureEvent = + RoutedEvent.Register( + "PullGesture", RoutingStrategies.Bubble, typeof(Gestures)); + + public static readonly RoutedEvent PullGestureEndedEvent = + RoutedEvent.Register( + "PullGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); + static Gestures() { InputElement.PointerPressedEvent.RouteFinished.Subscribe(PointerPressed); diff --git a/src/Avalonia.Base/Input/PullGestureEventArgs.cs b/src/Avalonia.Base/Input/PullGestureEventArgs.cs new file mode 100644 index 0000000000..57ad24f7a3 --- /dev/null +++ b/src/Avalonia.Base/Input/PullGestureEventArgs.cs @@ -0,0 +1,43 @@ +using System; +using Avalonia.Interactivity; + +namespace Avalonia.Input +{ + public class PullGestureEventArgs : RoutedEventArgs + { + public int Id { get; } + public Vector Delta { get; } + public PullDirection PullDirection { get; } + + private static int _nextId = 1; + + public static int GetNextFreeId() => _nextId++; + + public PullGestureEventArgs(int id, Vector delta, PullDirection pullDirection) : base(Gestures.PullGestureEvent) + { + Id = id; + Delta = delta; + PullDirection = pullDirection; + } + } + + public class PullGestureEndedEventArgs : RoutedEventArgs + { + public int Id { get; } + public PullDirection PullDirection { get; } + + public PullGestureEndedEventArgs(int id, PullDirection pullDirection) : base(Gestures.PullGestureEndedEvent) + { + Id = id; + PullDirection = pullDirection; + } + } + + public enum PullDirection + { + TopToBottom, + BottomToTop, + LeftToRight, + RightToLeft + } +} diff --git a/src/Avalonia.Base/Input/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/PullGestureRecognizer.cs new file mode 100644 index 0000000000..bbbded44fa --- /dev/null +++ b/src/Avalonia.Base/Input/PullGestureRecognizer.cs @@ -0,0 +1,152 @@ +using Avalonia.Input.GestureRecognizers; + +namespace Avalonia.Input +{ + public class PullGestureRecognizer : StyledElement, IGestureRecognizer + { + private IInputElement? _target; + private IGestureRecognizerActionsDispatcher? _actions; + private Point _initialPosition; + private int _gestureId; + private IPointer? _tracking; + private PullDirection _pullDirection; + + /// + /// Defines the property. + /// + public static readonly DirectProperty PullDirectionProperty = + AvaloniaProperty.RegisterDirect( + nameof(PullDirection), + o => o.PullDirection, + (o, v) => o.PullDirection = v); + + public PullDirection PullDirection + { + get => _pullDirection; + set => SetAndRaise(PullDirectionProperty, ref _pullDirection, value); + } + + public PullGestureRecognizer(PullDirection pullDirection) + { + PullDirection = pullDirection; + } + + public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) + { + _target = target; + _actions = actions; + + _target?.AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); + _target?.AddHandler(InputElement.PointerReleasedEvent, OnPointerReleased, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); + } + + private void OnPointerPressed(object? sender, PointerPressedEventArgs e) + { + PointerPressed(e); + } + + private void OnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + PointerReleased(e); + } + + public void PointerCaptureLost(IPointer pointer) + { + if (_tracking == pointer) + { + EndPull(); + } + } + + public void PointerMoved(PointerEventArgs e) + { + if (_tracking == e.Pointer) + { + var currentPosition = e.GetPosition(_target); + _actions!.Capture(e.Pointer, this); + + Vector delta = default; + switch (PullDirection) + { + case PullDirection.TopToBottom: + if (currentPosition.Y > _initialPosition.Y) + { + delta = new Vector(0, currentPosition.Y - _initialPosition.Y); + } + break; + case PullDirection.BottomToTop: + if (currentPosition.Y < _initialPosition.Y) + { + delta = new Vector(0, _initialPosition.Y - currentPosition.Y); + } + break; + case PullDirection.LeftToRight: + if (currentPosition.X > _initialPosition.X) + { + delta = new Vector(currentPosition.X - _initialPosition.X, 0); + } + break; + case PullDirection.RightToLeft: + if (currentPosition.X < _initialPosition.X) + { + delta = new Vector(_initialPosition.X - currentPosition.X, 0); + } + break; + } + + _target?.RaiseEvent(new PullGestureEventArgs(_gestureId, delta, PullDirection)); + } + } + + public void PointerPressed(PointerPressedEventArgs e) + { + if (_target != null && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) + { + var position = e.GetPosition(_target); + + var canPull = false; + + var bounds = _target.Bounds; + + switch (PullDirection) + { + case PullDirection.TopToBottom: + canPull = position.Y < bounds.Height * 0.1; + break; + case PullDirection.BottomToTop: + canPull = position.Y > bounds.Height - (bounds.Height * 0.1); + break; + case PullDirection.LeftToRight: + canPull = position.X < bounds.Width * 0.1; + break; + case PullDirection.RightToLeft: + canPull = position.X > bounds.Width - (bounds.Width * 0.1); + break; + } + + if (canPull) + { + _gestureId = PullGestureEventArgs.GetNextFreeId(); + _tracking = e.Pointer; + _initialPosition = position; + } + } + } + + public void PointerReleased(PointerReleasedEventArgs e) + { + if (_tracking == e.Pointer) + { + EndPull(); + } + } + + private void EndPull() + { + _tracking = null; + _initialPosition = default; + + _target?.RaiseEvent(new PullGestureEndedEventArgs(_gestureId, PullDirection)); + } + } +} diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshContainer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshContainer.cs new file mode 100644 index 0000000000..b882cf5a0f --- /dev/null +++ b/src/Avalonia.Controls/PullToRefresh/RefreshContainer.cs @@ -0,0 +1,221 @@ +using System; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.PullToRefresh; +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace Avalonia.Controls +{ + public class RefreshContainer : ContentControl + { + internal const int DefaultPullDimensionSize = 100; + + private readonly bool _hasDefaultRefreshInfoProviderAdapter; + + private ScrollViewerIRefreshInfoProviderAdapter _refreshInfoProviderAdapter; + private RefreshInfoProvider _refreshInfoProvider; + private IDisposable _visualizerSizeSubscription; + private Grid? _visualizerPresenter; + private RefreshVisualizer _refreshVisualizer; + + public static readonly RoutedEvent RefreshRequestedEvent = + RoutedEvent.Register(nameof(RefreshRequested), RoutingStrategies.Bubble); + + internal static readonly DirectProperty RefreshInfoProviderAdapterProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshInfoProviderAdapter), + (s) => s.RefreshInfoProviderAdapter, (s, o) => s.RefreshInfoProviderAdapter = o); + + public static readonly DirectProperty RefreshVisualizerProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshVisualizer), + s => s.RefreshVisualizer, (s, o) => s.RefreshVisualizer = o); + + public static readonly StyledProperty PullDirectionProperty = + AvaloniaProperty.Register(nameof(PullDirection), PullDirection.TopToBottom); + + public ScrollViewerIRefreshInfoProviderAdapter RefreshInfoProviderAdapter + { + get => _refreshInfoProviderAdapter; set + { + SetAndRaise(RefreshInfoProviderAdapterProperty, ref _refreshInfoProviderAdapter, value); + } + } + + private bool _hasDefaultRefreshVisualizer; + + public RefreshVisualizer RefreshVisualizer + { + get => _refreshVisualizer; set + { + if (_refreshVisualizer != null) + { + _visualizerSizeSubscription?.Dispose(); + _refreshVisualizer.RefreshRequested -= Visualizer_RefreshRequested; + } + + SetAndRaise(RefreshVisualizerProperty, ref _refreshVisualizer, value); + } + } + + public PullDirection PullDirection + { + get => GetValue(PullDirectionProperty); + set => SetValue(PullDirectionProperty, value); + } + + public event EventHandler? RefreshRequested + { + add => AddHandler(RefreshRequestedEvent, value); + remove => RemoveHandler(RefreshRequestedEvent, value); + } + + public RefreshContainer() + { + _hasDefaultRefreshInfoProviderAdapter = true; + RefreshInfoProviderAdapter = new ScrollViewerIRefreshInfoProviderAdapter(PullDirection); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + _visualizerPresenter = e.NameScope.Find("PART_RefreshVisualizerPresenter"); + + if (_refreshVisualizer == null) + { + _hasDefaultRefreshVisualizer = true; + RefreshVisualizer = new RefreshVisualizer(); + } + else + { + _hasDefaultRefreshVisualizer = false; + RaisePropertyChanged(RefreshVisualizerProperty, null, _refreshVisualizer); + } + + OnPullDirectionChanged(); + } + + private void OnVisualizerSizeChanged(Rect obj) + { + if (_hasDefaultRefreshInfoProviderAdapter) + { + RefreshInfoProviderAdapter = new ScrollViewerIRefreshInfoProviderAdapter(PullDirection); + } + } + + private void Visualizer_RefreshRequested(object? sender, RefreshRequestedEventArgs e) + { + var ev = new RefreshRequestedEventArgs(e.GetRefreshCompletionDeferral(), RefreshRequestedEvent); + RaiseEvent(ev); + ev.DecrementCount(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == RefreshInfoProviderAdapterProperty) + { + if (_refreshInfoProvider != null) + { + _refreshVisualizer.RefreshInfoProvider = _refreshInfoProvider; + } + else + { + if (RefreshInfoProviderAdapter != null && _refreshVisualizer != null) + { + _refreshInfoProvider = RefreshInfoProviderAdapter.AdaptFromTree(this, _refreshVisualizer.Bounds.Size); + + if (_refreshInfoProvider != null) + { + _refreshVisualizer.RefreshInfoProvider = _refreshInfoProvider; + } + } + } + } + else if (change.Property == RefreshVisualizerProperty) + { + if (_visualizerPresenter != null) + { + _visualizerPresenter.Children.Clear(); + if (_refreshVisualizer != null) + { + _visualizerPresenter.Children.Add(_refreshVisualizer); + } + } + + if (_refreshVisualizer != null) + { + _refreshVisualizer.RefreshRequested += Visualizer_RefreshRequested; + _visualizerSizeSubscription = _refreshVisualizer.GetObservable(Control.BoundsProperty).Subscribe(OnVisualizerSizeChanged); + } + } + else if (change.Property == PullDirectionProperty) + { + OnPullDirectionChanged(); + } + } + + private void OnPullDirectionChanged() + { + if (_visualizerPresenter != null) + { + switch (PullDirection) + { + case PullDirection.TopToBottom: + _visualizerPresenter.VerticalAlignment = Layout.VerticalAlignment.Top; + _visualizerPresenter.HorizontalAlignment = Layout.HorizontalAlignment.Stretch; + if (_hasDefaultRefreshVisualizer) + { + _refreshVisualizer.PullDirection = PullDirection.TopToBottom; + _refreshVisualizer.Height = DefaultPullDimensionSize; + _refreshVisualizer.Width = double.NaN; + } + break; + case PullDirection.BottomToTop: + _visualizerPresenter.VerticalAlignment = Layout.VerticalAlignment.Bottom; + _visualizerPresenter.HorizontalAlignment = Layout.HorizontalAlignment.Stretch; + if (_hasDefaultRefreshVisualizer) + { + _refreshVisualizer.PullDirection = PullDirection.BottomToTop; + _refreshVisualizer.Height = DefaultPullDimensionSize; + _refreshVisualizer.Width = double.NaN; + } + break; + case PullDirection.LeftToRight: + _visualizerPresenter.VerticalAlignment = Layout.VerticalAlignment.Stretch; + _visualizerPresenter.HorizontalAlignment = Layout.HorizontalAlignment.Left; + if (_hasDefaultRefreshVisualizer) + { + _refreshVisualizer.PullDirection = PullDirection.LeftToRight; + _refreshVisualizer.Width = DefaultPullDimensionSize; + _refreshVisualizer.Height = double.NaN; + } + break; + case PullDirection.RightToLeft: + _visualizerPresenter.VerticalAlignment = Layout.VerticalAlignment.Stretch; + _visualizerPresenter.HorizontalAlignment = Layout.HorizontalAlignment.Right; + if (_hasDefaultRefreshVisualizer) + { + _refreshVisualizer.PullDirection = PullDirection.RightToLeft; + _refreshVisualizer.Width = DefaultPullDimensionSize; + _refreshVisualizer.Height = double.NaN; + } + break; + } + + if (_hasDefaultRefreshInfoProviderAdapter && + _hasDefaultRefreshVisualizer && + _refreshVisualizer.Bounds.Height == DefaultPullDimensionSize && + _refreshVisualizer.Bounds.Width == DefaultPullDimensionSize) + { + RefreshInfoProviderAdapter = new ScrollViewerIRefreshInfoProviderAdapter(PullDirection); + } + } + } + + public void RequestRefresh() + { + _refreshVisualizer?.RequestRefresh(); + } + } +} diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs b/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs new file mode 100644 index 0000000000..3fc32cdd08 --- /dev/null +++ b/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs @@ -0,0 +1,129 @@ +using System; +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace Avalonia.Controls.PullToRefresh +{ + public class RefreshInfoProvider : Interactive + { + internal const double ExecutionRatio = 0.8; + + private readonly PullDirection _refreshPullDirection; + private readonly Size _refreshVisualizerSize; + + private readonly Visual _visual; + private bool _isInteractingForRefresh; + private double _interactionRatio; + private bool _entered; + + public DirectProperty IsInteractingForRefreshProperty = + AvaloniaProperty.RegisterDirect(nameof(IsInteractingForRefresh), + s => s.IsInteractingForRefresh, (s, o) => s.IsInteractingForRefresh = o); + + public DirectProperty InteractionRatioProperty = + AvaloniaProperty.RegisterDirect(nameof(InteractionRatio), + s => s.InteractionRatio, (s, o) => s.InteractionRatio = o); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent RefreshStartedEvent = + RoutedEvent.Register(nameof(RefreshStarted), RoutingStrategies.Bubble); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent RefreshCompletedEvent = + RoutedEvent.Register(nameof(RefreshCompleted), RoutingStrategies.Bubble); + + public bool PeekingMode { get; internal set; } + + public bool IsInteractingForRefresh + { + get => _isInteractingForRefresh; internal set + { + var isInteractingForRefresh = value && !PeekingMode; + + if (isInteractingForRefresh != _isInteractingForRefresh) + { + SetAndRaise(IsInteractingForRefreshProperty, ref _isInteractingForRefresh, isInteractingForRefresh); + } + } + } + + public double InteractionRatio + { + get => _interactionRatio; + set + { + SetAndRaise(InteractionRatioProperty, ref _interactionRatio, value); + } + } + + internal Visual Visual => _visual; + + public event EventHandler RefreshStarted + { + add => AddHandler(RefreshStartedEvent, value); + remove => RemoveHandler(RefreshStartedEvent, value); + } + + public event EventHandler RefreshCompleted + { + add => AddHandler(RefreshCompletedEvent, value); + remove => RemoveHandler(RefreshCompletedEvent, value); + } + + internal void InteractingStateEntered(object sender, PullGestureEventArgs e) + { + if (!_entered) + { + IsInteractingForRefresh = true; + _entered = true; + } + + ValuesChanged(e.Delta); + } + + internal void InteractingStateExited(object sender, PullGestureEndedEventArgs e) + { + IsInteractingForRefresh = false; + _entered = false; + + ValuesChanged(default); + } + + + public RefreshInfoProvider(PullDirection refreshPullDirection, Size refreshVIsualizerSize, Visual visual) + { + _refreshPullDirection = refreshPullDirection; + _refreshVisualizerSize = refreshVIsualizerSize; + _visual = visual; + } + + public void OnRefreshStarted() + { + RaiseEvent(new RoutedEventArgs(RefreshStartedEvent)); + } + + public void OnRefreshCompleted() + { + RaiseEvent(new RoutedEventArgs(RefreshCompletedEvent)); + } + + internal void ValuesChanged(Vector value) + { + switch (_refreshPullDirection) + { + case PullDirection.TopToBottom: + case PullDirection.BottomToTop: + InteractionRatio = _refreshVisualizerSize.Height == 0 ? 1 : Math.Min(1, value.Y / _refreshVisualizerSize.Height); + break; + case PullDirection.LeftToRight: + case PullDirection.RightToLeft: + InteractionRatio = _refreshVisualizerSize.Height == 0 ? 1 : Math.Min(1, value.X / _refreshVisualizerSize.Width); + break; + } + } + } +} diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs new file mode 100644 index 0000000000..81c613443d --- /dev/null +++ b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs @@ -0,0 +1,544 @@ +using System; +using System.Reactive.Linq; +using System.Threading; +using Avalonia.Animation; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.PullToRefresh; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Media; + +namespace Avalonia.Controls +{ + public class RefreshVisualizer : ContentControl + { + private const int DefaultIndicatorSize = 24; + private const double MinimumIndicatorOpacity = 0.4; + private const string ArrowPathData = "M18.6195264,3.31842271 C19.0080059,3.31842271 19.3290603,3.60710385 19.3798716,3.9816481 L19.3868766,4.08577298 L19.3868766,6.97963208 C19.3868766,7.36811161 19.0981955,7.68916605 18.7236513,7.73997735 L18.6195264,7.74698235 L15.7256673,7.74698235 C15.3018714,7.74698235 14.958317,7.40342793 14.958317,6.97963208 C14.958317,6.59115255 15.2469981,6.27009811 15.6215424,6.21928681 L15.7256673,6.21228181 L16.7044011,6.21182461 C13.7917384,3.87107476 9.52212532,4.05209336 6.81933829,6.75488039 C3.92253872,9.65167996 3.92253872,14.34832 6.81933829,17.2451196 C9.71613786,20.1419192 14.4127779,20.1419192 17.3095775,17.2451196 C19.0725398,15.4821573 19.8106555,12.9925923 19.3476248,10.58925 C19.2674502,10.173107 19.5398064,9.77076216 19.9559494,9.69058758 C20.3720923,9.610413 20.7744372,9.88276918 20.8546118,10.2989121 C21.4129973,13.1971899 20.5217103,16.2033812 18.3947747,18.3303168 C14.8986373,21.8264542 9.23027854,21.8264542 5.73414113,18.3303168 C2.23800371,14.8341794 2.23800371,9.16582064 5.73414113,5.66968323 C9.05475132,2.34907304 14.3349409,2.18235834 17.8523166,5.16953912 L17.8521761,4.08577298 C17.8521761,3.66197713 18.1957305,3.31842271 18.6195264,3.31842271 Z"; + private double _executingRatio = 0.8; + + private RotateTransform _visualizerRotateTransform; + private TranslateTransform _contentTranslateTransform; + private RefreshVisualizerState _refreshVisualizerState; + private RefreshInfoProvider _refreshInfoProvider; + private IDisposable _isInteractingSubscription; + private IDisposable _interactionRatioSubscription; + private bool _isInteractingForRefresh; + private Grid? _root; + private Control _content; + private RefreshVisualizerOrientation _orientation; + private float _startingRotationAngle; + private double _interactionRatio; + + private bool IsPullDirectionVertical => PullDirection == PullDirection.TopToBottom || PullDirection == PullDirection.BottomToTop; + private bool IsPullDirectionFar => PullDirection == PullDirection.BottomToTop || PullDirection == PullDirection.RightToLeft; + + public static readonly StyledProperty PullDirectionProperty = + AvaloniaProperty.Register(nameof(PullDirection), PullDirection.TopToBottom); + public static readonly RoutedEvent RefreshRequestedEvent = + RoutedEvent.Register(nameof(RefreshRequested), RoutingStrategies.Bubble); + + public static readonly DirectProperty RefreshVisualizerStateProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshVisualizerState), + s => s.RefreshVisualizerState); + + public static readonly DirectProperty OrientationProperty = + AvaloniaProperty.RegisterDirect(nameof(Orientation), + s => s.Orientation, (s, o) => s.Orientation = o); + + public DirectProperty RefreshInfoProviderProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshInfoProvider), + s => s.RefreshInfoProvider, (s, o) => s.RefreshInfoProvider = o); + + public RefreshVisualizerState RefreshVisualizerState + { + get + { + return _refreshVisualizerState; + } + private set + { + SetAndRaise(RefreshVisualizerStateProperty, ref _refreshVisualizerState, value); + UpdateContent(); + } + } + + public RefreshVisualizerOrientation Orientation + { + get + { + return _orientation; + } + set + { + SetAndRaise(OrientationProperty, ref _orientation, value); + } + } + + internal PullDirection PullDirection + { + get => GetValue(PullDirectionProperty); + set + { + SetValue(PullDirectionProperty, value); + + OnOrientationChanged(); + + UpdateContent(); + } + } + + public RefreshInfoProvider RefreshInfoProvider + { + get => _refreshInfoProvider; internal set + { + if (_refreshInfoProvider != null) + { + _refreshInfoProvider.RenderTransform = null; + } + SetAndRaise(RefreshInfoProviderProperty, ref _refreshInfoProvider, value); + } + } + + public event EventHandler? RefreshRequested + { + add => AddHandler(RefreshRequestedEvent, value); + remove => RemoveHandler(RefreshRequestedEvent, value); + } + + public RefreshVisualizer() + { + _visualizerRotateTransform = new RotateTransform(); + _contentTranslateTransform = new TranslateTransform(); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + _root = e.NameScope.Find("PART_Root"); + + if (_root != null) + { + if (_content == null) + { + Content = new PathIcon() + { + Data = PathGeometry.Parse(ArrowPathData), + Height = DefaultIndicatorSize, + Width = DefaultIndicatorSize + }; + } + else + { + RaisePropertyChanged(ContentProperty, null, Content); + } + } + + OnOrientationChanged(); + + UpdateContent(); + } + + private void UpdateContent() + { + if (_content != null) + { + switch (RefreshVisualizerState) + { + case RefreshVisualizerState.Idle: + _content.Classes.Remove("refreshing"); + _root.Classes.Remove("pending"); + _content.RenderTransform = _visualizerRotateTransform; + _content.Opacity = MinimumIndicatorOpacity; + _visualizerRotateTransform.Angle = _startingRotationAngle; + _contentTranslateTransform.X = 0; + _contentTranslateTransform.Y = 0; + break; + case RefreshVisualizerState.Interacting: + _content.Classes.Remove("refreshing"); + _root.Classes.Remove("pending"); + _content.RenderTransform = _visualizerRotateTransform; + _content.Opacity = MinimumIndicatorOpacity; + _visualizerRotateTransform.Angle = _startingRotationAngle + (_interactionRatio * 360); + _content.Height = DefaultIndicatorSize; + _content.Width = DefaultIndicatorSize; + if (IsPullDirectionVertical) + { + _contentTranslateTransform.X = 0; + _contentTranslateTransform.Y = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Height; + } + else + { + _contentTranslateTransform.Y = 0; + _contentTranslateTransform.X = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Width; + } + break; + case RefreshVisualizerState.Pending: + _content.Classes.Remove("refreshing"); + _content.Opacity = 1; + _content.RenderTransform = _visualizerRotateTransform; + if (IsPullDirectionVertical) + { + _contentTranslateTransform.X = 0; + _contentTranslateTransform.Y = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Height; + } + else + { + _contentTranslateTransform.Y = 0; + _contentTranslateTransform.X = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Width; + } + + _root.Classes.Add("pending"); + break; + case RefreshVisualizerState.Refreshing: + _root.Classes.Remove("pending"); + _content.Classes.Add("refreshing"); + _content.Opacity = 1; + _content.Height = DefaultIndicatorSize; + _content.Width = DefaultIndicatorSize; + break; + case RefreshVisualizerState.Peeking: + _root.Classes.Remove("pending"); + _content.Opacity = 1; + _visualizerRotateTransform.Angle += _startingRotationAngle; + break; + } + } + } + + public void RequestRefresh() + { + RefreshVisualizerState = RefreshVisualizerState.Refreshing; + RefreshInfoProvider?.OnRefreshStarted(); + + RaiseRefreshRequested(); + } + + private void RefreshCompleted() + { + RefreshVisualizerState = RefreshVisualizerState.Idle; + + RefreshInfoProvider?.OnRefreshCompleted(); + } + + private void RaiseRefreshRequested() + { + var refreshArgs = new RefreshRequestedEventArgs(RefreshCompleted, RefreshRequestedEvent); + + refreshArgs.IncrementCount(); + + RaiseEvent(refreshArgs); + + refreshArgs.DecrementCount(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == RefreshInfoProviderProperty) + { + OnRefreshInfoProviderChanged(); + } + else if (change.Property == ContentProperty) + { + if (_root != null) + { + if (_content == null) + { + _content = new PathIcon() + { + Data = PathGeometry.Parse(ArrowPathData), + Height = DefaultIndicatorSize, + Width = DefaultIndicatorSize + }; + + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(_visualizerRotateTransform); + + _content.RenderTransform = _visualizerRotateTransform; + _root.RenderTransform = _contentTranslateTransform; + + var transition = new Transitions + { + new DoubleTransition() + { + Property = OpacityProperty, + Duration = TimeSpan.FromSeconds(0.5) + }, + }; + + _content.Transitions = transition; + } + + var scalingGrid = new Grid(); + scalingGrid.VerticalAlignment = Layout.VerticalAlignment.Center; + scalingGrid.HorizontalAlignment = Layout.HorizontalAlignment.Center; + scalingGrid.RenderTransform = new ScaleTransform(); + + scalingGrid.Children.Add(_content); + + _root.Children.Insert(0, scalingGrid); + _content.VerticalAlignment = Layout.VerticalAlignment.Center; + _content.HorizontalAlignment = Layout.HorizontalAlignment.Center; + } + + UpdateContent(); + } + else if (change.Property == OrientationProperty) + { + OnOrientationChanged(); + + UpdateContent(); + } + else if (change.Property == BoundsProperty) + { + if (_content != null) + { + var parent = _content.Parent as Control; + switch (PullDirection) + { + case PullDirection.TopToBottom: + parent.Margin = new Thickness(0, -Bounds.Height - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize), 0, 0); + break; + case PullDirection.BottomToTop: + parent.Margin = new Thickness(0, 0, 0, -Bounds.Height - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize)); + break; + case PullDirection.LeftToRight: + parent.Margin = new Thickness(-Bounds.Width - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize), 0, 0, 0); + break; + case PullDirection.RightToLeft: + parent.Margin = new Thickness(0, 0, -Bounds.Width - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize), 0); + break; + } + } + } + } + + private void OnOrientationChanged() + { + switch (_orientation) + { + case RefreshVisualizerOrientation.Auto: + switch (PullDirection) + { + case PullDirection.TopToBottom: + case PullDirection.BottomToTop: + _startingRotationAngle = 0.0f; + break; + case PullDirection.LeftToRight: + _startingRotationAngle = 270; + break; + case PullDirection.RightToLeft: + _startingRotationAngle = 90; + break; + } + break; + case RefreshVisualizerOrientation.Normal: + _startingRotationAngle = 0.0f; + break; + case RefreshVisualizerOrientation.Rotate90DegreesCounterclockwise: + _startingRotationAngle = 270; + break; + case RefreshVisualizerOrientation.Rotate270DegreesCounterclockwise: + _startingRotationAngle = 90; + break; + } + } + + private void OnRefreshInfoProviderChanged() + { + _isInteractingSubscription?.Dispose(); + _isInteractingSubscription = null; + _interactionRatioSubscription?.Dispose(); + _interactionRatioSubscription = null; + + if (_refreshInfoProvider != null) + { + _isInteractingSubscription = _refreshInfoProvider.GetObservable(RefreshInfoProvider.IsInteractingForRefreshProperty) + .Subscribe(InteractingForRefreshObserver); + + _interactionRatioSubscription = _refreshInfoProvider.GetObservable(RefreshInfoProvider.InteractionRatioProperty) + .Subscribe(InteractionRatioObserver); + + var visual = _refreshInfoProvider.Visual; + visual.RenderTransform = _contentTranslateTransform; + + _executingRatio = RefreshInfoProvider.ExecutionRatio; + } + else + { + _executingRatio = 1; + } + } + + private void InteractionRatioObserver(double obj) + { + var wasAtZero = _interactionRatio == 0.0; + _interactionRatio = obj; + + if (_isInteractingForRefresh) + { + if (RefreshVisualizerState == RefreshVisualizerState.Idle) + { + if (wasAtZero) + { + if (_interactionRatio > _executingRatio) + { + RefreshVisualizerState = RefreshVisualizerState.Pending; + } + else if (_interactionRatio > 0) + { + RefreshVisualizerState = RefreshVisualizerState.Interacting; + } + } + else if (_interactionRatio > 0) + { + RefreshVisualizerState = RefreshVisualizerState.Peeking; + } + } + else if (RefreshVisualizerState == RefreshVisualizerState.Interacting) + { + if (_interactionRatio <= 0) + { + RefreshVisualizerState = RefreshVisualizerState.Idle; + } + else if (_interactionRatio > _executingRatio) + { + RefreshVisualizerState = RefreshVisualizerState.Pending; + } + else + { + UpdateContent(); + } + } + else if (RefreshVisualizerState == RefreshVisualizerState.Pending) + { + if (_interactionRatio <= _executingRatio) + { + RefreshVisualizerState = RefreshVisualizerState.Interacting; + } + else if (_interactionRatio <= 0) + { + RefreshVisualizerState = RefreshVisualizerState.Idle; + } + else + { + UpdateContent(); + } + } + } + else + { + if (RefreshVisualizerState != RefreshVisualizerState.Refreshing) + { + if (_interactionRatio > 0) + { + RefreshVisualizerState = RefreshVisualizerState.Peeking; + } + else + { + RefreshVisualizerState = RefreshVisualizerState.Idle; + } + } + } + } + + private void InteractingForRefreshObserver(bool obj) + { + _isInteractingForRefresh = obj; + + if (!_isInteractingForRefresh) + { + switch (_refreshVisualizerState) + { + case RefreshVisualizerState.Pending: + RequestRefresh(); + break; + case RefreshVisualizerState.Refreshing: + // We don't want to interrupt a currently executing refresh. + break; + default: + RefreshVisualizerState = RefreshVisualizerState.Idle; + break; + } + } + } + } + + public enum RefreshVisualizerState + { + Idle, + Peeking, + Interacting, + Pending, + Refreshing + } + + public enum RefreshVisualizerOrientation + { + Auto, + Normal, + Rotate90DegreesCounterclockwise, + Rotate270DegreesCounterclockwise + } + + public class RefreshRequestedEventArgs : RoutedEventArgs + { + private RefreshCompletionDeferral _refreshCompletionDeferral; + + public RefreshCompletionDeferral GetRefreshCompletionDeferral() + { + return _refreshCompletionDeferral.Get(); + } + + public RefreshRequestedEventArgs(Action deferredAction, RoutedEvent? routedEvent) : base(routedEvent) + { + _refreshCompletionDeferral = new RefreshCompletionDeferral(deferredAction); + } + + public RefreshRequestedEventArgs(RefreshCompletionDeferral completionDeferral, RoutedEvent? routedEvent) : base(routedEvent) + { + _refreshCompletionDeferral = completionDeferral; + } + + internal void IncrementCount() + { + _refreshCompletionDeferral?.Get(); + } + + internal void DecrementCount() + { + _refreshCompletionDeferral?.Complete(); + } + } + + public class RefreshCompletionDeferral + { + private Action _deferredAction; + private int _deferCount; + + public RefreshCompletionDeferral(Action deferredAction) + { + _deferredAction = deferredAction; + } + + public void Complete() + { + Interlocked.Decrement(ref _deferCount); + + if (_deferCount == 0) + { + _deferredAction?.Invoke(); + } + } + + public RefreshCompletionDeferral Get() + { + Interlocked.Increment(ref _deferCount); + + return this; + } + } +} diff --git a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs new file mode 100644 index 0000000000..0b0c8c99b2 --- /dev/null +++ b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs @@ -0,0 +1,227 @@ +using System; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace Avalonia.Controls.PullToRefresh +{ + public class ScrollViewerIRefreshInfoProviderAdapter + { + private const int MaxSearchDepth = 10; + private const int InitialOffsetThreshold = 1; + + private PullDirection _refreshPullDirection; + private ScrollViewer _scrollViewer; + private RefreshInfoProvider _refreshInfoProvider; + private PullGestureRecognizer _pullGestureRecognizer; + private InputElement? _interactionSource; + private bool _isVisualizerInteractionSourceAttached; + + public ScrollViewerIRefreshInfoProviderAdapter(PullDirection pullDirection) + { + _refreshPullDirection = pullDirection; + } + + public RefreshInfoProvider AdaptFromTree(IVisual root, Size refreshVIsualizerSize) + { + if (root is ScrollViewer scrollViewer) + { + return Adapt(scrollViewer, refreshVIsualizerSize); + } + else + { + int depth = 0; + while (depth < MaxSearchDepth) + { + var scroll = AdaptFromTreeRecursiveHelper(root, depth); + + if (scroll != null) + { + return Adapt(scroll, refreshVIsualizerSize); + } + + depth++; + } + } + + ScrollViewer AdaptFromTreeRecursiveHelper(IVisual root, int depth) + { + if (depth == 0) + { + foreach (var child in root.VisualChildren) + { + if (child is ScrollViewer viewer) + { + return viewer; + } + } + } + else + { + foreach (var child in root.VisualChildren) + { + var viewer = AdaptFromTreeRecursiveHelper(child, depth - 1); + if (viewer != null) + { + return viewer; + } + } + } + + return null; + } + + return null; + } + + public RefreshInfoProvider Adapt(ScrollViewer adaptee, Size refreshVIsualizerSize) + { + if (adaptee == null) + { + throw new ArgumentNullException(nameof(adaptee), "Adaptee cannot be null"); + } + + if (_scrollViewer != null) + { + CleanUpScrollViewer(); + } + + if (_refreshInfoProvider != null && _interactionSource != null) + { + _interactionSource.RemoveHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource.RemoveHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + } + + _refreshInfoProvider = null; + _scrollViewer = adaptee; + + if (_scrollViewer.Content == null) + { + throw new ArgumentException(nameof(adaptee), "Adaptee's content property cannot be null."); + } + + var content = adaptee.Content as Visual; + + if (content == null) + { + throw new ArgumentException(nameof(adaptee), "Adaptee's content property must be a Visual"); + } + + if (content.GetVisualParent() == null) + { + _scrollViewer.Loaded += ScrollViewer_Loaded; + } + else + { + ScrollViewer_Loaded(null, null); + + if (content.Parent is not InputElement) + { + throw new ArgumentException(nameof(adaptee), "Adaptee's content's parent must be a InputElement"); + } + } + + _refreshInfoProvider = new RefreshInfoProvider(_refreshPullDirection, refreshVIsualizerSize, content); + + _pullGestureRecognizer = new PullGestureRecognizer(_refreshPullDirection); + + if (_interactionSource != null) + { + _interactionSource.GestureRecognizers.Add(_pullGestureRecognizer); + _interactionSource.AddHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource.AddHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + _isVisualizerInteractionSourceAttached = true; + } + + _scrollViewer.PointerPressed += ScrollViewer_PointerPressed; + _scrollViewer.PointerReleased += ScrollViewer_PointerReleased; + _scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged; + + return _refreshInfoProvider; + } + + private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) + { + if (_isVisualizerInteractionSourceAttached && _refreshInfoProvider != null && _refreshInfoProvider.IsInteractingForRefresh) + { + if (!IsWithinOffsetThreashold()) + { + _refreshInfoProvider.IsInteractingForRefresh = false; + } + } + } + + private void ScrollViewer_Loaded(object sender, RoutedEventArgs e) + { + var content = _scrollViewer.Content as Visual; + if (content == null) + { + throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content property must be a Visual"); + } + + if (content.Parent is not InputElement) + { + throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content parent must be an InputElement"); + } + + MakeInteractionSource(content.Parent as InputElement); + + _scrollViewer.Loaded -= ScrollViewer_Loaded; + } + + private void MakeInteractionSource(InputElement element) + { + _interactionSource = element; + + if (_pullGestureRecognizer != null) + { + element.GestureRecognizers.Add(_pullGestureRecognizer); + _interactionSource.AddHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource.AddHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + _isVisualizerInteractionSourceAttached = true; + } + } + + private void ScrollViewer_PointerReleased(object sender, PointerReleasedEventArgs e) + { + if (_refreshInfoProvider != null) + { + _refreshInfoProvider.IsInteractingForRefresh = false; + } + } + + private void ScrollViewer_PointerPressed(object sender, PointerPressedEventArgs e) + { + _refreshInfoProvider.PeekingMode = !IsWithinOffsetThreashold(); + } + + private bool IsWithinOffsetThreashold() + { + if (_scrollViewer != null) + { + var offset = _scrollViewer.Offset; + + switch (_refreshPullDirection) + { + case PullDirection.TopToBottom: + return offset.Y < InitialOffsetThreshold; + case PullDirection.LeftToRight: + return offset.X < InitialOffsetThreshold; + case PullDirection.RightToLeft: + return offset.X > _scrollViewer.Extent.Width - _scrollViewer.Viewport.Width - InitialOffsetThreshold; + case PullDirection.BottomToTop: + return offset.Y > _scrollViewer.Extent.Height - _scrollViewer.Viewport.Height - InitialOffsetThreshold; + } + } + + return false; + } + + private void CleanUpScrollViewer() + { + _scrollViewer.PointerPressed -= ScrollViewer_PointerPressed; + _scrollViewer.PointerReleased -= ScrollViewer_PointerReleased; + _scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged; + } + } +} diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml index 7e3c8673f5..d9be545801 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml @@ -637,6 +637,10 @@ - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml index 7917315e19..e80b137b2d 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml @@ -633,5 +633,9 @@ + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 5383aa3180..5d38b055b3 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -69,6 +69,8 @@ + + diff --git a/src/Avalonia.Themes.Fluent/Controls/RefreshContainer.xaml b/src/Avalonia.Themes.Fluent/Controls/RefreshContainer.xaml new file mode 100644 index 0000000000..97002c25bd --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Controls/RefreshContainer.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml b/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml new file mode 100644 index 0000000000..388ca814f8 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml b/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml index 1843abebfd..c748775a12 100644 --- a/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml @@ -33,6 +33,9 @@ + + + diff --git a/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml b/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml index 6247815303..43acb16be5 100644 --- a/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml @@ -33,6 +33,8 @@ - + + + diff --git a/src/Avalonia.Themes.Simple/Controls/RefreshContainer.xaml b/src/Avalonia.Themes.Simple/Controls/RefreshContainer.xaml new file mode 100644 index 0000000000..326b217068 --- /dev/null +++ b/src/Avalonia.Themes.Simple/Controls/RefreshContainer.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml b/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml new file mode 100644 index 0000000000..0b12fe133d --- /dev/null +++ b/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml index 4aefa0136c..4bad556338 100644 --- a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml +++ b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml @@ -65,6 +65,8 @@ + + From 68fd64f1640b657eb3f15987ff9e077eb7f366ac Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 17 Nov 2022 15:29:39 +0000 Subject: [PATCH 028/173] Use Composition Api for animation --- .../Pages/RefreshContainerPage.axaml | 2 + .../Input/PullGestureRecognizer.cs | 2 +- .../PullToRefresh/RefreshContainer.cs | 48 +-- .../PullToRefresh/RefreshInfoProvider.cs | 13 +- .../PullToRefresh/RefreshVisualizer.cs | 282 +++++++++++------- ...ScrollViewerIRefreshInfoProviderAdapter.cs | 91 ++++-- .../Controls/RefreshVisualizer.xaml | 32 +- 7 files changed, 277 insertions(+), 193 deletions(-) diff --git a/samples/ControlCatalog/Pages/RefreshContainerPage.axaml b/samples/ControlCatalog/Pages/RefreshContainerPage.axaml index 0123251bd0..d467d14427 100644 --- a/samples/ControlCatalog/Pages/RefreshContainerPage.axaml +++ b/samples/ControlCatalog/Pages/RefreshContainerPage.axaml @@ -2,9 +2,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:viewModels="using:ControlCatalog.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:DataType="viewModels:RefreshContainerViewModel" x:Class="ControlCatalog.Pages.RefreshContainerPage"> RefreshRequestedEvent = RoutedEvent.Register(nameof(RefreshRequested), RoutingStrategies.Bubble); - internal static readonly DirectProperty RefreshInfoProviderAdapterProperty = - AvaloniaProperty.RegisterDirect(nameof(RefreshInfoProviderAdapter), + internal static readonly DirectProperty RefreshInfoProviderAdapterProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshInfoProviderAdapter), (s) => s.RefreshInfoProviderAdapter, (s, o) => s.RefreshInfoProviderAdapter = o); - public static readonly DirectProperty RefreshVisualizerProperty = - AvaloniaProperty.RegisterDirect(nameof(RefreshVisualizer), + public static readonly DirectProperty RefreshVisualizerProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshVisualizer), s => s.RefreshVisualizer, (s, o) => s.RefreshVisualizer = o); public static readonly StyledProperty PullDirectionProperty = AvaloniaProperty.Register(nameof(PullDirection), PullDirection.TopToBottom); - public ScrollViewerIRefreshInfoProviderAdapter RefreshInfoProviderAdapter + public ScrollViewerIRefreshInfoProviderAdapter? RefreshInfoProviderAdapter { get => _refreshInfoProviderAdapter; set { @@ -42,7 +42,7 @@ namespace Avalonia.Controls private bool _hasDefaultRefreshVisualizer; - public RefreshVisualizer RefreshVisualizer + public RefreshVisualizer? RefreshVisualizer { get => _refreshVisualizer; set { @@ -88,7 +88,7 @@ namespace Avalonia.Controls else { _hasDefaultRefreshVisualizer = false; - RaisePropertyChanged(RefreshVisualizerProperty, null, _refreshVisualizer); + RaisePropertyChanged(RefreshVisualizerProperty, default, _refreshVisualizer); } OnPullDirectionChanged(); @@ -115,19 +115,23 @@ namespace Avalonia.Controls if (change.Property == RefreshInfoProviderAdapterProperty) { - if (_refreshInfoProvider != null) - { - _refreshVisualizer.RefreshInfoProvider = _refreshInfoProvider; - } - else + if (_refreshVisualizer != null) { - if (RefreshInfoProviderAdapter != null && _refreshVisualizer != null) + if (_refreshInfoProvider != null) { - _refreshInfoProvider = RefreshInfoProviderAdapter.AdaptFromTree(this, _refreshVisualizer.Bounds.Size); - - if (_refreshInfoProvider != null) + _refreshVisualizer.RefreshInfoProvider = _refreshInfoProvider; + } + else + { + if (RefreshInfoProviderAdapter != null && _refreshVisualizer != null) { - _refreshVisualizer.RefreshInfoProvider = _refreshInfoProvider; + _refreshInfoProvider = RefreshInfoProviderAdapter?.AdaptFromTree(this, _refreshVisualizer.Bounds.Size); + + if (_refreshInfoProvider != null) + { + _refreshVisualizer.RefreshInfoProvider = _refreshInfoProvider; + RefreshInfoProviderAdapter?.SetAnimations(_refreshVisualizer); + } } } } @@ -157,7 +161,7 @@ namespace Avalonia.Controls private void OnPullDirectionChanged() { - if (_visualizerPresenter != null) + if (_visualizerPresenter != null && _refreshVisualizer != null) { switch (PullDirection) { diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs b/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs index 3fc32cdd08..506b48f6e2 100644 --- a/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs +++ b/src/Avalonia.Controls/PullToRefresh/RefreshInfoProvider.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Rendering.Composition; namespace Avalonia.Controls.PullToRefresh { @@ -11,7 +12,7 @@ namespace Avalonia.Controls.PullToRefresh private readonly PullDirection _refreshPullDirection; private readonly Size _refreshVisualizerSize; - private readonly Visual _visual; + private readonly CompositionVisual? _visual; private bool _isInteractingForRefresh; private double _interactionRatio; private bool _entered; @@ -60,7 +61,7 @@ namespace Avalonia.Controls.PullToRefresh } } - internal Visual Visual => _visual; + internal CompositionVisual? Visual => _visual; public event EventHandler RefreshStarted { @@ -74,7 +75,7 @@ namespace Avalonia.Controls.PullToRefresh remove => RemoveHandler(RefreshCompletedEvent, value); } - internal void InteractingStateEntered(object sender, PullGestureEventArgs e) + internal void InteractingStateEntered(object? sender, PullGestureEventArgs e) { if (!_entered) { @@ -85,7 +86,7 @@ namespace Avalonia.Controls.PullToRefresh ValuesChanged(e.Delta); } - internal void InteractingStateExited(object sender, PullGestureEndedEventArgs e) + internal void InteractingStateExited(object? sender, PullGestureEndedEventArgs e) { IsInteractingForRefresh = false; _entered = false; @@ -94,10 +95,10 @@ namespace Avalonia.Controls.PullToRefresh } - public RefreshInfoProvider(PullDirection refreshPullDirection, Size refreshVIsualizerSize, Visual visual) + public RefreshInfoProvider(PullDirection refreshPullDirection, Size? refreshVIsualizerSize, CompositionVisual? visual) { _refreshPullDirection = refreshPullDirection; - _refreshVisualizerSize = refreshVIsualizerSize; + _refreshVisualizerSize = refreshVIsualizerSize ?? default; _visual = visual; } diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs index 81c613443d..5d0eee7478 100644 --- a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs +++ b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using System.Reactive.Linq; using System.Threading; using Avalonia.Animation; @@ -7,25 +8,24 @@ using Avalonia.Controls.PullToRefresh; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; +using Avalonia.Rendering.Composition; namespace Avalonia.Controls { public class RefreshVisualizer : ContentControl { private const int DefaultIndicatorSize = 24; - private const double MinimumIndicatorOpacity = 0.4; + private const float MinimumIndicatorOpacity = 0.4f; private const string ArrowPathData = "M18.6195264,3.31842271 C19.0080059,3.31842271 19.3290603,3.60710385 19.3798716,3.9816481 L19.3868766,4.08577298 L19.3868766,6.97963208 C19.3868766,7.36811161 19.0981955,7.68916605 18.7236513,7.73997735 L18.6195264,7.74698235 L15.7256673,7.74698235 C15.3018714,7.74698235 14.958317,7.40342793 14.958317,6.97963208 C14.958317,6.59115255 15.2469981,6.27009811 15.6215424,6.21928681 L15.7256673,6.21228181 L16.7044011,6.21182461 C13.7917384,3.87107476 9.52212532,4.05209336 6.81933829,6.75488039 C3.92253872,9.65167996 3.92253872,14.34832 6.81933829,17.2451196 C9.71613786,20.1419192 14.4127779,20.1419192 17.3095775,17.2451196 C19.0725398,15.4821573 19.8106555,12.9925923 19.3476248,10.58925 C19.2674502,10.173107 19.5398064,9.77076216 19.9559494,9.69058758 C20.3720923,9.610413 20.7744372,9.88276918 20.8546118,10.2989121 C21.4129973,13.1971899 20.5217103,16.2033812 18.3947747,18.3303168 C14.8986373,21.8264542 9.23027854,21.8264542 5.73414113,18.3303168 C2.23800371,14.8341794 2.23800371,9.16582064 5.73414113,5.66968323 C9.05475132,2.34907304 14.3349409,2.18235834 17.8523166,5.16953912 L17.8521761,4.08577298 C17.8521761,3.66197713 18.1957305,3.31842271 18.6195264,3.31842271 Z"; private double _executingRatio = 0.8; - private RotateTransform _visualizerRotateTransform; - private TranslateTransform _contentTranslateTransform; private RefreshVisualizerState _refreshVisualizerState; - private RefreshInfoProvider _refreshInfoProvider; - private IDisposable _isInteractingSubscription; - private IDisposable _interactionRatioSubscription; + private RefreshInfoProvider? _refreshInfoProvider; + private IDisposable? _isInteractingSubscription; + private IDisposable? _interactionRatioSubscription; private bool _isInteractingForRefresh; private Grid? _root; - private Control _content; + private Control? _content; private RefreshVisualizerOrientation _orientation; private float _startingRotationAngle; private double _interactionRatio; @@ -46,9 +46,11 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterDirect(nameof(Orientation), s => s.Orientation, (s, o) => s.Orientation = o); - public DirectProperty RefreshInfoProviderProperty = - AvaloniaProperty.RegisterDirect(nameof(RefreshInfoProvider), + public DirectProperty RefreshInfoProviderProperty = + AvaloniaProperty.RegisterDirect(nameof(RefreshInfoProvider), s => s.RefreshInfoProvider, (s, o) => s.RefreshInfoProvider = o); + private Vector3 _defaultOffset; + private bool _played; public RefreshVisualizerState RefreshVisualizerState { @@ -88,7 +90,7 @@ namespace Avalonia.Controls } } - public RefreshInfoProvider RefreshInfoProvider + public RefreshInfoProvider? RefreshInfoProvider { get => _refreshInfoProvider; internal set { @@ -106,20 +108,17 @@ namespace Avalonia.Controls remove => RemoveHandler(RefreshRequestedEvent, value); } - public RefreshVisualizer() - { - _visualizerRotateTransform = new RotateTransform(); - _contentTranslateTransform = new TranslateTransform(); - } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); + this.ClipToBounds = false; + _root = e.NameScope.Find("PART_Root"); if (_root != null) { + _content = Content as Control; if (_content == null) { Content = new PathIcon() @@ -140,69 +139,113 @@ namespace Avalonia.Controls UpdateContent(); } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + UpdateContent(); + } + private void UpdateContent() { - if (_content != null) + if (_content != null && _root != null) { - switch (RefreshVisualizerState) + var root = _root; + var visual = _refreshInfoProvider?.Visual; + var contentVisual = ElementComposition.GetElementVisual(_content); + var visualizerVisual = ElementComposition.GetElementVisual(this); + if (visual != null && contentVisual != null && visualizerVisual != null) { - case RefreshVisualizerState.Idle: - _content.Classes.Remove("refreshing"); - _root.Classes.Remove("pending"); - _content.RenderTransform = _visualizerRotateTransform; - _content.Opacity = MinimumIndicatorOpacity; - _visualizerRotateTransform.Angle = _startingRotationAngle; - _contentTranslateTransform.X = 0; - _contentTranslateTransform.Y = 0; - break; - case RefreshVisualizerState.Interacting: - _content.Classes.Remove("refreshing"); - _root.Classes.Remove("pending"); - _content.RenderTransform = _visualizerRotateTransform; - _content.Opacity = MinimumIndicatorOpacity; - _visualizerRotateTransform.Angle = _startingRotationAngle + (_interactionRatio * 360); - _content.Height = DefaultIndicatorSize; - _content.Width = DefaultIndicatorSize; - if (IsPullDirectionVertical) - { - _contentTranslateTransform.X = 0; - _contentTranslateTransform.Y = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Height; - } - else - { - _contentTranslateTransform.Y = 0; - _contentTranslateTransform.X = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Width; - } - break; - case RefreshVisualizerState.Pending: - _content.Classes.Remove("refreshing"); - _content.Opacity = 1; - _content.RenderTransform = _visualizerRotateTransform; - if (IsPullDirectionVertical) - { - _contentTranslateTransform.X = 0; - _contentTranslateTransform.Y = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Height; - } - else - { - _contentTranslateTransform.Y = 0; - _contentTranslateTransform.X = _interactionRatio * (IsPullDirectionFar ? -1 : 1) * _root.Bounds.Width; - } - - _root.Classes.Add("pending"); - break; - case RefreshVisualizerState.Refreshing: - _root.Classes.Remove("pending"); - _content.Classes.Add("refreshing"); - _content.Opacity = 1; - _content.Height = DefaultIndicatorSize; - _content.Width = DefaultIndicatorSize; - break; - case RefreshVisualizerState.Peeking: - _root.Classes.Remove("pending"); - _content.Opacity = 1; - _visualizerRotateTransform.Angle += _startingRotationAngle; - break; + contentVisual.CenterPoint = new Vector3((float)(_content.Bounds.Width / 2), (float)(_content.Bounds.Height / 2), 0); + switch (RefreshVisualizerState) + { + case RefreshVisualizerState.Idle: + _played = false; + contentVisual.Opacity = MinimumIndicatorOpacity; + contentVisual.RotationAngle = (float)(_startingRotationAngle * Math.PI / 180f); + visualizerVisual.Offset = IsPullDirectionVertical ? + new Vector3(visualizerVisual.Offset.X, 0, 0) : + new Vector3(0, visualizerVisual.Offset.Y, 0); + contentVisual.Scale = new Vector3(0.9f, 0.9f, 0.9f); + visual.Offset = default; + break; + case RefreshVisualizerState.Interacting: + _played = false; + contentVisual.Opacity = MinimumIndicatorOpacity; + contentVisual.RotationAngle = (float)((_startingRotationAngle + (_interactionRatio * 360)) * Math.PI / 180f); + Vector3 offset = default; + if (IsPullDirectionVertical) + { + offset = new Vector3(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + } + else + { + offset = new Vector3((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + } + visual.Offset = offset; + visualizerVisual.Offset = IsPullDirectionVertical ? + new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : + new Vector3(offset.X, visualizerVisual.Offset.Y, 0); + contentVisual.Scale = new Vector3(0.9f, 0.9f, 0.9f); + break; + case RefreshVisualizerState.Pending: + contentVisual.Opacity = 1; + contentVisual.RotationAngle = (float)((_startingRotationAngle + 360) * Math.PI / 180f); + if (IsPullDirectionVertical) + { + offset = new Vector3(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + } + else + { + offset = new Vector3((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + } + visual.Offset = offset; + visualizerVisual.Offset = IsPullDirectionVertical ? + new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : + new Vector3(offset.X, visualizerVisual.Offset.Y, 0); + + if (!_played) + { + _played = true; + var scaleAnimation = contentVisual.Compositor!.CreateVector3KeyFrameAnimation(); + scaleAnimation.Target = "Scale"; + scaleAnimation.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 1)); + scaleAnimation.InsertKeyFrame(1f, new Vector3(1f, 1f, 1)); + scaleAnimation.Duration = TimeSpan.FromSeconds(0.3); + + contentVisual.StartAnimation("Scale", scaleAnimation); + } + break; + case RefreshVisualizerState.Refreshing: + var rotateAnimation = contentVisual.Compositor!.CreateScalarKeyFrameAnimation(); + rotateAnimation.Target = "RotationAngle"; + rotateAnimation.InsertKeyFrame(0, (float)(0)); + rotateAnimation.InsertKeyFrame(0.5f, (float)(Math.PI)); + rotateAnimation.InsertKeyFrame(1, (float)(2 * Math.PI)); + rotateAnimation.Duration = TimeSpan.FromSeconds(1); + rotateAnimation.IterationCount = 1000; + + contentVisual.StartAnimation("RotationAngle", rotateAnimation); + contentVisual.Opacity = 1; + contentVisual.Scale = new Vector3(0.9f, 0.9f, 0.9f); + if (IsPullDirectionVertical) + { + offset = new Vector3(0, (float)(_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0); + } + else + { + offset = new Vector3((float)(_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0); + } + visual.Offset = offset; + visualizerVisual.Offset = IsPullDirectionVertical ? + new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : + new Vector3(offset.X, visualizerVisual.Offset.Y, 0); + break; + case RefreshVisualizerState.Peeking: + contentVisual.Opacity = 1; + contentVisual.RotationAngle = (float)(_startingRotationAngle * Math.PI / 180f); + break; + } } } } @@ -254,12 +297,6 @@ namespace Avalonia.Controls Width = DefaultIndicatorSize }; - var transformGroup = new TransformGroup(); - transformGroup.Children.Add(_visualizerRotateTransform); - - _content.RenderTransform = _visualizerRotateTransform; - _root.RenderTransform = _contentTranslateTransform; - var transition = new Transitions { new DoubleTransition() @@ -270,16 +307,42 @@ namespace Avalonia.Controls }; _content.Transitions = transition; - } - var scalingGrid = new Grid(); - scalingGrid.VerticalAlignment = Layout.VerticalAlignment.Center; - scalingGrid.HorizontalAlignment = Layout.HorizontalAlignment.Center; - scalingGrid.RenderTransform = new ScaleTransform(); - - scalingGrid.Children.Add(_content); + _content.Loaded += (s, e) => + { + var composition = ElementComposition.GetElementVisual(_content); + var compositor = composition!.Compositor; + composition.Opacity = 0; + + var smoothRotationAnimation + = compositor.CreateScalarKeyFrameAnimation(); + smoothRotationAnimation.Target = "RotationAngle"; + smoothRotationAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue"); + smoothRotationAnimation.Duration = TimeSpan.FromMilliseconds(100); + + var offsetAnimation = compositor.CreateVector3KeyFrameAnimation(); + offsetAnimation.Target = "Offset"; + offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue"); + offsetAnimation.Duration = TimeSpan.FromMilliseconds(150); + + var scaleAnimation + = compositor.CreateVector3KeyFrameAnimation(); + scaleAnimation.Target = "Scale"; + scaleAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue"); + scaleAnimation.Duration = TimeSpan.FromMilliseconds(100); + + var animation = compositor.CreateImplicitAnimationCollection(); + animation["RotationAngle"] = smoothRotationAnimation; + animation["Offset"] = offsetAnimation; + animation["Scale"] = scaleAnimation; + + composition.ImplicitAnimations = animation; + + UpdateContent(); + }; + } - _root.Children.Insert(0, scalingGrid); + _root.Children.Add(_content); _content.VerticalAlignment = Layout.VerticalAlignment.Center; _content.HorizontalAlignment = Layout.HorizontalAlignment.Center; } @@ -294,25 +357,23 @@ namespace Avalonia.Controls } else if (change.Property == BoundsProperty) { - if (_content != null) + switch (PullDirection) { - var parent = _content.Parent as Control; - switch (PullDirection) - { - case PullDirection.TopToBottom: - parent.Margin = new Thickness(0, -Bounds.Height - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize), 0, 0); - break; - case PullDirection.BottomToTop: - parent.Margin = new Thickness(0, 0, 0, -Bounds.Height - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize)); - break; - case PullDirection.LeftToRight: - parent.Margin = new Thickness(-Bounds.Width - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize), 0, 0, 0); - break; - case PullDirection.RightToLeft: - parent.Margin = new Thickness(0, 0, -Bounds.Width - DefaultIndicatorSize - (0.5 * DefaultIndicatorSize), 0); - break; - } + case PullDirection.TopToBottom: + RenderTransform = new TranslateTransform(0, -Bounds.Height); + break; + case PullDirection.BottomToTop: + RenderTransform = new TranslateTransform(0, Bounds.Height); + break; + case PullDirection.LeftToRight: + RenderTransform = new TranslateTransform(-Bounds.Width, 0); + break; + case PullDirection.RightToLeft: + RenderTransform = new TranslateTransform(Bounds.Width, 0); + break; } + + UpdateContent(); } } @@ -354,16 +415,15 @@ namespace Avalonia.Controls _interactionRatioSubscription?.Dispose(); _interactionRatioSubscription = null; - if (_refreshInfoProvider != null) + if (RefreshInfoProvider != null) { - _isInteractingSubscription = _refreshInfoProvider.GetObservable(RefreshInfoProvider.IsInteractingForRefreshProperty) + _isInteractingSubscription = RefreshInfoProvider.GetObservable(RefreshInfoProvider.IsInteractingForRefreshProperty) .Subscribe(InteractingForRefreshObserver); - _interactionRatioSubscription = _refreshInfoProvider.GetObservable(RefreshInfoProvider.InteractionRatioProperty) + _interactionRatioSubscription = RefreshInfoProvider.GetObservable(RefreshInfoProvider.InteractionRatioProperty) .Subscribe(InteractionRatioObserver); - var visual = _refreshInfoProvider.Visual; - visual.RenderTransform = _contentTranslateTransform; + var visual = RefreshInfoProvider.Visual; _executingRatio = RefreshInfoProvider.ExecutionRatio; } diff --git a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs index 0b0c8c99b2..d8e90c01c0 100644 --- a/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs +++ b/src/Avalonia.Controls/PullToRefresh/ScrollViewerIRefreshInfoProviderAdapter.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Rendering.Composition; using Avalonia.VisualTree; namespace Avalonia.Controls.PullToRefresh @@ -11,9 +12,9 @@ namespace Avalonia.Controls.PullToRefresh private const int InitialOffsetThreshold = 1; private PullDirection _refreshPullDirection; - private ScrollViewer _scrollViewer; - private RefreshInfoProvider _refreshInfoProvider; - private PullGestureRecognizer _pullGestureRecognizer; + private ScrollViewer? _scrollViewer; + private RefreshInfoProvider? _refreshInfoProvider; + private PullGestureRecognizer? _pullGestureRecognizer; private InputElement? _interactionSource; private bool _isVisualizerInteractionSourceAttached; @@ -22,7 +23,7 @@ namespace Avalonia.Controls.PullToRefresh _refreshPullDirection = pullDirection; } - public RefreshInfoProvider AdaptFromTree(IVisual root, Size refreshVIsualizerSize) + public RefreshInfoProvider? AdaptFromTree(IVisual root, Size? refreshVIsualizerSize) { if (root is ScrollViewer scrollViewer) { @@ -44,7 +45,7 @@ namespace Avalonia.Controls.PullToRefresh } } - ScrollViewer AdaptFromTreeRecursiveHelper(IVisual root, int depth) + ScrollViewer? AdaptFromTreeRecursiveHelper(IVisual root, int depth) { if (depth == 0) { @@ -74,7 +75,7 @@ namespace Avalonia.Controls.PullToRefresh return null; } - public RefreshInfoProvider Adapt(ScrollViewer adaptee, Size refreshVIsualizerSize) + public RefreshInfoProvider Adapt(ScrollViewer adaptee, Size? refreshVIsualizerSize) { if (adaptee == null) { @@ -121,7 +122,7 @@ namespace Avalonia.Controls.PullToRefresh } } - _refreshInfoProvider = new RefreshInfoProvider(_refreshPullDirection, refreshVIsualizerSize, content); + _refreshInfoProvider = new RefreshInfoProvider(_refreshPullDirection, refreshVIsualizerSize, ElementComposition.GetElementVisual(content)); _pullGestureRecognizer = new PullGestureRecognizer(_refreshPullDirection); @@ -140,7 +141,7 @@ namespace Avalonia.Controls.PullToRefresh return _refreshInfoProvider; } - private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) + private void ScrollViewer_ScrollChanged(object? sender, ScrollChangedEventArgs e) { if (_isVisualizerInteractionSourceAttached && _refreshInfoProvider != null && _refreshInfoProvider.IsInteractingForRefresh) { @@ -151,9 +152,46 @@ namespace Avalonia.Controls.PullToRefresh } } - private void ScrollViewer_Loaded(object sender, RoutedEventArgs e) + public void SetAnimations(RefreshVisualizer refreshVisualizer) { - var content = _scrollViewer.Content as Visual; + var visualizerComposition = ElementComposition.GetElementVisual(refreshVisualizer); + if (visualizerComposition != null) + { + var compositor = visualizerComposition.Compositor; + + var offsetAnimation = compositor.CreateVector3KeyFrameAnimation(); + offsetAnimation.Target = "Offset"; + offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue"); + offsetAnimation.Duration = TimeSpan.FromMilliseconds(150); + + var animation = compositor.CreateImplicitAnimationCollection(); + animation["Offset"] = offsetAnimation; + visualizerComposition.ImplicitAnimations = animation; + } + + if(_scrollViewer != null && _scrollViewer.Content is Visual visual) + { + var scollContentComposition = ElementComposition.GetElementVisual(visual); + + if(scollContentComposition != null) + { + var compositor = scollContentComposition.Compositor; + + var offsetAnimation = compositor.CreateVector3KeyFrameAnimation(); + offsetAnimation.Target = "Offset"; + offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue"); + offsetAnimation.Duration = TimeSpan.FromMilliseconds(150); + + var animation = compositor.CreateImplicitAnimationCollection(); + animation["Offset"] = offsetAnimation; + scollContentComposition.ImplicitAnimations = animation; + } + } + } + + private void ScrollViewer_Loaded(object? sender, RoutedEventArgs? e) + { + var content = _scrollViewer?.Content as Visual; if (content == null) { throw new ArgumentException(nameof(_scrollViewer), "Adaptee's content property must be a Visual"); @@ -166,23 +204,26 @@ namespace Avalonia.Controls.PullToRefresh MakeInteractionSource(content.Parent as InputElement); - _scrollViewer.Loaded -= ScrollViewer_Loaded; + if (_scrollViewer != null) + { + _scrollViewer.Loaded -= ScrollViewer_Loaded; + } } - private void MakeInteractionSource(InputElement element) + private void MakeInteractionSource(InputElement? element) { _interactionSource = element; - if (_pullGestureRecognizer != null) + if (_pullGestureRecognizer != null && _refreshInfoProvider != null) { - element.GestureRecognizers.Add(_pullGestureRecognizer); - _interactionSource.AddHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); - _interactionSource.AddHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); + element?.GestureRecognizers.Add(_pullGestureRecognizer); + _interactionSource?.AddHandler(Gestures.PullGestureEvent, _refreshInfoProvider.InteractingStateEntered); + _interactionSource?.AddHandler(Gestures.PullGestureEndedEvent, _refreshInfoProvider.InteractingStateExited); _isVisualizerInteractionSourceAttached = true; } } - private void ScrollViewer_PointerReleased(object sender, PointerReleasedEventArgs e) + private void ScrollViewer_PointerReleased(object? sender, PointerReleasedEventArgs e) { if (_refreshInfoProvider != null) { @@ -190,9 +231,12 @@ namespace Avalonia.Controls.PullToRefresh } } - private void ScrollViewer_PointerPressed(object sender, PointerPressedEventArgs e) + private void ScrollViewer_PointerPressed(object? sender, PointerPressedEventArgs e) { - _refreshInfoProvider.PeekingMode = !IsWithinOffsetThreashold(); + if (_refreshInfoProvider != null) + { + _refreshInfoProvider.PeekingMode = !IsWithinOffsetThreashold(); + } } private bool IsWithinOffsetThreashold() @@ -219,9 +263,12 @@ namespace Avalonia.Controls.PullToRefresh private void CleanUpScrollViewer() { - _scrollViewer.PointerPressed -= ScrollViewer_PointerPressed; - _scrollViewer.PointerReleased -= ScrollViewer_PointerReleased; - _scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged; + if (_scrollViewer != null) + { + _scrollViewer.PointerPressed -= ScrollViewer_PointerPressed; + _scrollViewer.PointerReleased -= ScrollViewer_PointerReleased; + _scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged; + } } } } diff --git a/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml b/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml index 388ca814f8..3fafabb667 100644 --- a/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml @@ -14,37 +14,7 @@ - - - - - + + + + + + diff --git a/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml b/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml index 0b12fe133d..bd7e43530a 100644 --- a/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml +++ b/src/Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml @@ -18,51 +18,10 @@ MinHeight="80" Background="{TemplateBinding Background}"> - - From 6e1ab5f4578ad7a4edb968f87b203a714c64c0a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 18 Nov 2022 12:37:31 +0000 Subject: [PATCH 030/173] remove debug stuff --- src/Avalonia.Base/Input/PullGestureRecognizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/PullGestureRecognizer.cs index 6939646f13..bbbded44fa 100644 --- a/src/Avalonia.Base/Input/PullGestureRecognizer.cs +++ b/src/Avalonia.Base/Input/PullGestureRecognizer.cs @@ -100,7 +100,7 @@ namespace Avalonia.Input public void PointerPressed(PointerPressedEventArgs e) { - if (_target != null)// && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) + if (_target != null && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) { var position = e.GetPosition(_target); From 2d7c8645d0e82ed71edce10472bb5d5b1360d1d2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 21 Nov 2022 01:14:43 +0600 Subject: [PATCH 031/173] Recalculate parent's child render nodes on visual tree attachment --- src/Avalonia.Base/Visual.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 69389def56..51fc143f72 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -432,6 +432,7 @@ namespace Avalonia OnAttachedToVisualTree(e); AttachedToVisualTree?.Invoke(this, e); InvalidateVisual(); + _visualRoot.Renderer.RecalculateChildren(_visualParent!); if (ZIndex != 0 && this.GetVisualParent() is Visual parent) parent.HasNonUniformZIndexChildren = true; From 7d6932bd5ef3fca26b44f47911a162212f4bdfeb Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 28 Nov 2022 17:04:14 +0000 Subject: [PATCH 032/173] add failing unit tests. --- .../WindowTests.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index 0cdde445d5..f73c3ac215 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -986,7 +986,46 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(SizeToContent.WidthAndHeight, target.SizeToContent); } } + + [Fact] + public void IsVisible_Should_Open_Window() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var target = new Window(); + var raised = false; + + target.Opened += (s, e) => raised = true; + target.IsVisible = true; + Assert.True(raised); + } + } + + [Fact] + public void IsVisible_Should_Close_DialogWindow() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var parent = new Window(); + parent.Show(); + + var target = new Window(); + + var raised = false; + + var task = target.ShowDialog(parent); + + target.Closed += (sender, args) => raised = true; + + target.IsVisible = false; + + Assert.True(raised); + + Assert.False(task.Result); + } + } + protected virtual void Show(Window window) { window.Show(); From a7a3df912a4984273fd20788de9afe13022d4d39 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 29 Nov 2022 09:46:27 +0000 Subject: [PATCH 033/173] implement isvisible controlling show / hide / close (for dialogs) --- src/Avalonia.Controls/Window.cs | 50 +++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 559d674c02..672099c3bb 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -72,6 +72,9 @@ namespace Avalonia.Controls private bool _isExtendedIntoWindowDecorations; private Thickness _windowDecorationMargin; private Thickness _offScreenMargin; + private bool _shown; + private bool _showingAsDialog; + private bool _inShowHideMethods; /// /// Defines the property. @@ -506,8 +509,14 @@ namespace Avalonia.Controls } Owner = null; + _showingAsDialog = false; + _shown = false; + _inShowHideMethods = true; PlatformImpl?.Dispose(); + + _inShowHideMethods = false; + } private bool ShouldCancelClose(CancelEventArgs? args = null) @@ -563,7 +572,7 @@ namespace Avalonia.Controls /// public override void Hide() { - if (!IsVisible) + if (!_shown) { return; } @@ -585,7 +594,10 @@ namespace Avalonia.Controls Owner = null; PlatformImpl?.Hide(); + _shown = false; + _inShowHideMethods = true; IsVisible = false; + _inShowHideMethods = false; } /// @@ -639,7 +651,7 @@ namespace Avalonia.Controls } } - if (IsVisible) + if (_shown) { return; } @@ -648,7 +660,10 @@ namespace Avalonia.Controls EnsureInitialized(); ApplyStyling(); + _inShowHideMethods = true; + _shown = true; IsVisible = true; + _inShowHideMethods = false; var initialSize = new Size( double.IsNaN(Width) ? Math.Max(MinWidth, ClientSize.Width) : Width, @@ -728,7 +743,11 @@ namespace Avalonia.Controls EnsureInitialized(); ApplyStyling(); + _shown = true; + _showingAsDialog = true; + _inShowHideMethods = true; IsVisible = true; + _inShowHideMethods = false; var initialSize = new Size( double.IsNaN(Width) ? ClientSize.Width : Width, @@ -999,6 +1018,33 @@ namespace Avalonia.Controls PlatformImpl?.SetSystemDecorations(typedNewValue); } + + if (change.Property == IsVisibleProperty) + { + if (!_inShowHideMethods) + { + var isVisible = change.GetNewValue(); + + if (_shown != isVisible) + { + if (!_shown) + { + ShowCore(null); + } + else + { + if (_showingAsDialog) + { + Close(false); + } + else + { + Hide(); + } + } + } + } + } } protected override AutomationPeer OnCreateAutomationPeer() From f4804551316b4141367f3ef3fa38c50d9a702fc0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 29 Nov 2022 12:39:24 +0100 Subject: [PATCH 034/173] Added failing test for #9565. And another (passing) test for checking that window order is correct when a child window is shown in fullscreen. --- samples/IntegrationTestApp/MainWindow.axaml | 7 +- .../IntegrationTestApp/MainWindow.axaml.cs | 3 + .../WindowTests_MacOS.cs | 74 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml index 038ced4e5c..801ff765c8 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml +++ b/samples/IntegrationTestApp/MainWindow.axaml @@ -17,11 +17,15 @@ - + + + WindowState: + + @@ -136,6 +140,7 @@ + diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs index c1acc7ca88..841947673a 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml.cs +++ b/samples/IntegrationTestApp/MainWindow.axaml.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Avalonia; @@ -178,6 +179,8 @@ namespace IntegrationTestApp ShowWindow(); if (source?.Name == "SendToBack") SendToBack(); + if (source?.Name == "EnterFullscreen") + WindowState = WindowState.FullScreen; if (source?.Name == "ExitFullscreen") WindowState = WindowState.Normal; if (source?.Name == "RestoreAll") diff --git a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs index 05ed0616a8..8f4417a451 100644 --- a/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs +++ b/tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using Avalonia.Controls; +using Avalonia.Utilities; using OpenQA.Selenium; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Interactions; @@ -114,6 +115,79 @@ namespace Avalonia.IntegrationTests.Appium Assert.Equal(1, secondaryWindowIndex); } } + + [PlatformFact(TestPlatforms.MacOS)] + public void WindowOrder_Owned_Dialog_Stays_InFront_Of_FullScreen_Parent() + { + var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); + + // Enter fullscreen + mainWindow.FindElementByAccessibilityId("EnterFullscreen").Click(); + + // Wait for fullscreen transition. + Thread.Sleep(1000); + + // Make sure we entered fullscreen. + var windowState = mainWindow.FindElementByAccessibilityId("WindowState"); + Assert.Equal("FullScreen", windowState.Text); + + // Open child window. + using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual)) + { + mainWindow.SendClick(); + var secondaryWindowIndex = GetWindowOrder("SecondaryWindow"); + Assert.Equal(1, secondaryWindowIndex); + } + + // Exit fullscreen by menu shortcut Command+R + mainWindow.FindElementByAccessibilityId("ExitFullscreen").Click(); + + // Wait for restore transition. + Thread.Sleep(1000); + + // Make sure we exited fullscreen. + mainWindow = _session.FindElementByAccessibilityId("MainWindow"); + windowState = mainWindow.FindElementByAccessibilityId("WindowState"); + Assert.Equal("Normal", windowState.Text); + } + + [PlatformFact(TestPlatforms.MacOS)] + public void Does_Not_Switch_Space_From_FullScreen_To_Main_Desktop_When_FullScreen_Window_Clicked() + { + // Issue #9565 + var mainWindow = _session.FindElementByAccessibilityId("MainWindow"); + AppiumWebElement windowState; + + // Open child window. + using (OpenWindow(new PixelSize(200, 100), ShowWindowMode.Owned, WindowStartupLocation.Manual)) + { + // Enter fullscreen + mainWindow.FindElementByAccessibilityId("EnterFullscreen").Click(); + + // Wait for fullscreen transition. + Thread.Sleep(1000); + + // Make sure we entered fullscreen. + mainWindow = _session.FindElementByAccessibilityId("MainWindow"); + windowState = mainWindow.FindElementByAccessibilityId("WindowState"); + Assert.Equal("FullScreen", windowState.Text); + + // Click on main window + mainWindow.Click(); + + // Failed here due to #9565: main window is no longer visible as the main space is now shown instead + // of the fullscreen space. + mainWindow.FindElementByAccessibilityId("ExitFullscreen").Click(); + + // Wait for restore transition. + Thread.Sleep(1000); + } + + // Make sure we exited fullscreen. + mainWindow = _session.FindElementByAccessibilityId("MainWindow"); + windowState = mainWindow.FindElementByAccessibilityId("WindowState"); + Assert.Equal("Normal", windowState.Text); + } [PlatformFact(TestPlatforms.MacOS)] public void WindowOrder_NonOwned_Window_Does_Not_Stay_InFront_Of_Parent() From d00f19d55671373ea0329e35e4a52afb3815364b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 29 Nov 2022 12:40:02 +0100 Subject: [PATCH 035/173] Fix #9565 - Only bring window to front if it's on the currently active space - Ensure correct order of child windows after fullscreen transition --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 2443965957..b6dacb6ce4 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -137,7 +137,11 @@ void WindowImpl::BringToFront() for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) { - (*iterator)->BringToFront(); + auto window = (*iterator)->Window; + + // #9565: Only bring window to front if it's on the currently active space + if ([window isOnActiveSpace]) + (*iterator)->BringToFront(); } } } @@ -161,6 +165,9 @@ void WindowImpl::StartStateTransition() { void WindowImpl::EndStateTransition() { _transitioningWindowState = false; + + // Ensure correct order of child windows after fullscreen transition. + BringToFront(); } SystemDecorations WindowImpl::Decorations() { From c884e38220df192290d05b5cf79bcbecdca11c4b Mon Sep 17 00:00:00 2001 From: Daniil Pavliuchyk Date: Wed, 30 Nov 2022 03:01:49 +0200 Subject: [PATCH 036/173] Fix typo --- src/Avalonia.Base/Rect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Rect.cs b/src/Avalonia.Base/Rect.cs index 7930228d99..a91b089a33 100644 --- a/src/Avalonia.Base/Rect.cs +++ b/src/Avalonia.Base/Rect.cs @@ -243,7 +243,7 @@ namespace Avalonia } /// - /// Determines whether a point in in the bounds of the rectangle. + /// Determines whether a point is in the bounds of the rectangle. /// /// The point. /// true if the point is in the bounds of the rectangle; otherwise false. From 98cf91c3853ad8935db98f6d7ab201e9983e9b84 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 02:25:48 -0500 Subject: [PATCH 037/173] Properly support relative Uris in StyleInclude/ResourceInclude --- .../AvaloniaXamlIlLanguageParseIntrinsics.cs | 2 +- .../XamlIncludeGroupTransformer.cs | 21 +++-- .../Converters/AvaloniaUriTypeConverter.cs | 2 +- .../Xaml/StyleIncludeTests.cs | 88 ++++++++++++++++++- 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index 64fdfe155d..925bf0a4fa 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -274,7 +274,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var uriText = text.Trim(); - var kind = ((!uriText?.StartsWith("/") == true) ? UriKind.Absolute : UriKind.Relative); + var kind = ((!uriText?.StartsWith("/") == true) ? UriKind.RelativeOrAbsolute : UriKind.Relative); if (string.IsNullOrWhiteSpace(uriText) || !Uri.TryCreate(uriText, kind, out var _)) { diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs index 09a65c9d64..f0895de5e2 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs @@ -36,34 +36,37 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer return context.ParseError($"Source property must be set on the \"{nodeTypeName}\" node.", node); } + // We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`. if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri - || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }) + || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath } + || sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind }) { // TODO: make it a compiler warning // Source value can be set with markup extension instead of the Uri object node, we don't support it here yet. return node; } - if (originalAssetPath.StartsWith("/")) + var uriPath = new Uri(originalAssetPath, (UriKind)uriKind); + if (!uriPath.IsAbsoluteUri) { var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null."); - originalAssetPath = baseUrl.Substring(0, baseUrl.LastIndexOf('/')) + originalAssetPath; + uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath); } - else if (!originalAssetPath.StartsWith("avares://")) + else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase)) { return context.ParseError( $"Avalonia supports only \"avares://\" sources or relative sources starting with \"/\" on the \"{nodeTypeName}\" node.", node); } - originalAssetPath = Uri.UnescapeDataString(new Uri(originalAssetPath).AbsoluteUri); - var assetPath = originalAssetPath.Replace("avares://", ""); + var assetPathUri = Uri.UnescapeDataString(uriPath.AbsoluteUri); + var assetPath = assetPathUri.Replace("avares://", ""); var assemblyNameSeparator = assetPath.IndexOf('/'); var assembly = assetPath.Substring(0, assemblyNameSeparator); var fullTypeName = Path.GetFileNameWithoutExtension(assetPath.Replace('/', '.')); - if (context.Documents.FirstOrDefault(d => string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase)) is {} targetDocument) + if (context.Documents.FirstOrDefault(d => string.Equals(d.Uri, assetPathUri, StringComparison.InvariantCultureIgnoreCase)) is {} targetDocument) { if (targetDocument.ClassType is not null) { @@ -72,7 +75,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer if (targetDocument.BuildMethod is null) { - return context.ParseError($"\"{originalAssetPath}\" cannot be instantiated.", node); + return context.ParseError($"\"{assetPathUri}\" cannot be instantiated.", node); } return FromMethod(context, targetDocument.BuildMethod, node); @@ -81,7 +84,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer if (context.Configuration.TypeSystem.FindAssembly(assembly) is not { } assetAssembly) { - return context.ParseError($"Assembly \"{assembly}\" was not found from the \"{originalAssetPath}\" source.", node); + return context.ParseError($"Assembly \"{assembly}\" was not found from the \"{assetPathUri}\" source.", node); } if (assetAssembly.FindType(fullTypeName) is { } type diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs index 3c3a718213..54c57d8b7b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs @@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.Converters if (s == null) return null; //On Unix Uri tries to interpret paths starting with "/" as file Uris - var kind = s.StartsWith("/") ? UriKind.Relative : UriKind.Absolute; + var kind = s.StartsWith("/") ? UriKind.Relative : UriKind.RelativeOrAbsolute; if (!Uri.TryCreate(s, kind, out var res)) throw new ArgumentException("Unable to parse URI: " + s); return res; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs index 4ff4405109..8eed5013a2 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs @@ -79,7 +79,35 @@ public class StyleIncludeTests } [Fact] - public void Relative_StyleInclude_Is_Resolved_With_Two_Files() + public void Relative_Back_StyleInclude_Is_Resolved_With_Two_Files() + { + var documents = new[] + { + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Subfolder/Style.xaml"), @" +"), + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Subfolder/Folder/Root.xaml"), @" + + + + +") + }; + + var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents); + var style = Assert.IsType"), + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Root.xaml"), @" + + + + +") + }; + + var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents); + var style = Assert.IsType"), + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Root.xaml"), @" + + + ") }; From 2e6fc861941f635be9ac47be7943472b08c10133 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 02:29:32 -0500 Subject: [PATCH 038/173] Make error clear when Source is not set on xaml include (#3788) --- src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs | 3 ++- src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs index 01db2c081f..d7eb328715 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs @@ -42,7 +42,8 @@ namespace Avalonia.Markup.Xaml.Styling if (_loaded == null) { _isLoading = true; - _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(Source, _baseUri); + var source = Source ?? throw new InvalidOperationException("ResourceInclude.Source must be set."); + _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(source, _baseUri); _isLoading = false; } diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 8af49b5480..2b75e0dd54 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -51,7 +51,8 @@ namespace Avalonia.Markup.Xaml.Styling if (_loaded == null) { _isLoading = true; - var loaded = (IStyle)AvaloniaXamlLoader.Load(Source, _baseUri); + var source = Source ?? throw new InvalidOperationException("StyleInclude.Source must be set."); + var loaded = (IStyle)AvaloniaXamlLoader.Load(source, _baseUri); _loaded = new[] { loaded }; _isLoading = false; } From 5e95e07ce29fbc475bc0324d2a83ed2837826ff7 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 02:33:37 -0500 Subject: [PATCH 039/173] Run xaml after CoreCompile --- packages/Avalonia/AvaloniaBuildTasks.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 1b822c14cf..5da76e414c 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -80,7 +80,7 @@ From dc5aa99d7aac152c0eec2666aacea51aab0a9968 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 03:21:22 -0500 Subject: [PATCH 040/173] Revert "Run xaml after CoreCompile" This reverts commit 5e95e07ce29fbc475bc0324d2a83ed2837826ff7. --- packages/Avalonia/AvaloniaBuildTasks.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 5da76e414c..1b822c14cf 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -80,7 +80,7 @@ From c0c67d8bdd9445622b833e323606a434d5e3a2c5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 03:46:39 -0500 Subject: [PATCH 041/173] Improve error messages and add type checking --- .../XamlIncludeGroupTransformer.cs | 88 ++++++++++++------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs index f0895de5e2..cc29d5ccb5 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs @@ -28,6 +28,13 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer } var nodeTypeName = objectNode.Type.GetClrType().Name; + var expectedLoadedType = objectNode.Type.GetClrType().GetAllProperties() + .FirstOrDefault(p => p.Name == "Loaded")?.PropertyType; + if (expectedLoadedType is null) + { + throw new InvalidOperationException($"\"{nodeTypeName}\".Loaded property is expected to be defined"); + } + if (valueNode.Manipulation is not XamlObjectInitializationNode { Manipulation: XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty @@ -56,8 +63,8 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase)) { return context.ParseError( - $"Avalonia supports only \"avares://\" sources or relative sources starting with \"/\" on the \"{nodeTypeName}\" node.", - node); + $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", + sourceUriNode, node); } var assetPathUri = Uri.UnescapeDataString(uriPath.AbsoluteUri); @@ -66,64 +73,79 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer var assembly = assetPath.Substring(0, assemblyNameSeparator); var fullTypeName = Path.GetFileNameWithoutExtension(assetPath.Replace('/', '.')); + // Search file in the current assembly among other XAML resources. if (context.Documents.FirstOrDefault(d => string.Equals(d.Uri, assetPathUri, StringComparison.InvariantCultureIgnoreCase)) is {} targetDocument) { - if (targetDocument.ClassType is not null) + if (targetDocument.BuildMethod is not null) { - return FromType(context, targetDocument.ClassType, node); + return FromMethod(context, targetDocument.BuildMethod, sourceUriNode, expectedLoadedType, node, assetPathUri, assembly); } - if (targetDocument.BuildMethod is null) + if (targetDocument.ClassType is not null) { - return context.ParseError($"\"{assetPathUri}\" cannot be instantiated.", node); + return FromType(context, targetDocument.ClassType, sourceUriNode, expectedLoadedType, node, assetPathUri, assembly); } - return FromMethod(context, targetDocument.BuildMethod, node); + return context.ParseError( + $"Unable to resolve XAML resource \"{assetPathUri}\" in the current assembly.", + sourceUriNode, node); } - + // If resource wasn't found in the current assembly, search in the others. if (context.Configuration.TypeSystem.FindAssembly(assembly) is not { } assetAssembly) { - return context.ParseError($"Assembly \"{assembly}\" was not found from the \"{assetPathUri}\" source.", node); + return context.ParseError($"Assembly \"{assembly}\" was not found from the \"{assetPathUri}\" source.", sourceUriNode, node); } - if (assetAssembly.FindType(fullTypeName) is { } type - && type.FindMethod(m => m.Name == "!XamlIlPopulate") is not null) + var avaResType = assetAssembly.FindType("CompiledAvaloniaXaml.!AvaloniaResources"); + if (avaResType is null) { - return FromType(context, type, node); + return context.ParseError( + $"Unable to resolve \"!AvaloniaResources\" type on \"{assembly}\" assembly.", sourceUriNode, node); } - else - { - var avaResType = assetAssembly.FindType("CompiledAvaloniaXaml.!AvaloniaResources"); - if (avaResType is null) - { - return context.ParseError( - $"Unable to resolve \"!AvaloniaResources\" type on \"{assembly}\" assembly.", node); - } - - var relativeName = "Build:" + assetPath.Substring(assemblyNameSeparator); - var buildMethod = avaResType.FindMethod(m => m.Name == relativeName); - if (buildMethod is null) - { - return context.ParseError( - $"Unable to resolve build method \"{relativeName}\" resource on the \"{assembly}\" assembly.", - node); - } - return FromMethod(context, buildMethod, node); + var relativeName = "Build:" + assetPath.Substring(assemblyNameSeparator); + var buildMethod = avaResType.FindMethod(m => m.Name == relativeName); + if (buildMethod is not null) + { + return FromMethod(context, buildMethod, sourceUriNode, expectedLoadedType, node, assetPathUri, assembly); + } + else if (assetAssembly.FindType(fullTypeName) is { } type) + { + return FromType(context, type, sourceUriNode, expectedLoadedType, node, assetPathUri, assembly); } - } - private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlLineInfo li) + return context.ParseError( + $"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly.", + sourceUriNode, node); + } + + private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li, + IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly) { + if (!expectedLoadedType.IsAssignableFrom(type)) + { + return context.ParseError( + $"Resource \"{assetPathUri}\" is defined as \"{type}\" type in the \"{assembly}\" assembly, but expected \"{expectedLoadedType}\".", + li, fallbackNode); + } + IXamlAstNode newObjNode = new XamlAstObjectNode(li, new XamlAstClrTypeReference(li, type, false)); newObjNode = new AvaloniaXamlIlConstructorServiceProviderTransformer().Transform(context, newObjNode); newObjNode = new ConstructableObjectTransformer().Transform(context, newObjNode); return new NewObjectTransformer().Transform(context, newObjNode); } - private static IXamlAstNode FromMethod(AstTransformationContext context, IXamlMethod method, IXamlLineInfo li) + private static IXamlAstNode FromMethod(AstTransformationContext context, IXamlMethod method, IXamlAstNode li, + IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly) { + if (!expectedLoadedType.IsAssignableFrom(method.ReturnType)) + { + return context.ParseError( + $"Resource \"{assetPathUri}\" is defined as \"{method.ReturnType}\" type in the \"{assembly}\" assembly, but expected \"{expectedLoadedType}\".", + li, fallbackNode); + } + var sp = context.Configuration.TypeMappings.ServiceProvider; return new XamlStaticOrTargetedReturnMethodCallNode(li, method, new[] { new NewServiceProviderNode(sp, li) }); From fdf76c5765ceb8f2c4191aa1257c2eb6cea3bccd Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Wed, 30 Nov 2022 14:50:27 +0200 Subject: [PATCH 042/173] Replace IVisual with Visual --- nukebuild/Numerge | 2 +- src/Avalonia.Base/Animation/CrossFade.cs | 2 +- src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs | 2 +- src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs | 2 +- src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs | 2 +- src/Avalonia.Base/Media/MatrixTransform.cs | 2 +- src/Avalonia.Base/Media/RotateTransform.cs | 2 +- src/Avalonia.Base/Media/ScaleTransform.cs | 2 +- src/Avalonia.Base/Media/SkewTransform.cs | 2 +- src/Avalonia.Base/Media/Transform.cs | 2 +- src/Avalonia.Base/Media/TranslateTransform.cs | 2 +- src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nukebuild/Numerge b/nukebuild/Numerge index aef10ae67d..fb92f917cd 160000 --- a/nukebuild/Numerge +++ b/nukebuild/Numerge @@ -1 +1 @@ -Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5 +Subproject commit fb92f917cd2d3aaec0d2294635d922184ff1e0fc diff --git a/src/Avalonia.Base/Animation/CrossFade.cs b/src/Avalonia.Base/Animation/CrossFade.cs index 608a0880ec..a229bc7ce6 100644 --- a/src/Avalonia.Base/Animation/CrossFade.cs +++ b/src/Avalonia.Base/Animation/CrossFade.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; namespace Avalonia.Animation { /// - /// Defines a cross-fade animation between two s. + /// Defines a cross-fade animation between two s. /// public class CrossFade : IPageTransition { diff --git a/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs index c4508c3f5c..d56711ad68 100644 --- a/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs @@ -7,7 +7,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media.Imaging { /// - /// A bitmap that holds the rendering of a . + /// A bitmap that holds the rendering of a . /// public class RenderTargetBitmap : Bitmap, IDisposable, IRenderTarget { diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs index d5ff2b8317..4478504eca 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs @@ -3,7 +3,7 @@ namespace Avalonia.Media.Immutable { /// - /// Represents a transform on an . + /// Represents a transform on an . /// public class ImmutableTransform : ITransform { diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 1e0133c9b7..9b443391c5 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -4,7 +4,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media.Immutable { /// - /// Paints an area with an . + /// Paints an area with an . /// internal class ImmutableVisualBrush : ImmutableTileBrush, IVisualBrush { diff --git a/src/Avalonia.Base/Media/MatrixTransform.cs b/src/Avalonia.Base/Media/MatrixTransform.cs index 4e60e1e290..c61acb730c 100644 --- a/src/Avalonia.Base/Media/MatrixTransform.cs +++ b/src/Avalonia.Base/Media/MatrixTransform.cs @@ -4,7 +4,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media { /// - /// Transforms an according to a . + /// Transforms an according to a . /// public class MatrixTransform : Transform { diff --git a/src/Avalonia.Base/Media/RotateTransform.cs b/src/Avalonia.Base/Media/RotateTransform.cs index 126bb7c274..3bd409149c 100644 --- a/src/Avalonia.Base/Media/RotateTransform.cs +++ b/src/Avalonia.Base/Media/RotateTransform.cs @@ -4,7 +4,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media { /// - /// Rotates an . + /// Rotates an . /// public class RotateTransform : Transform { diff --git a/src/Avalonia.Base/Media/ScaleTransform.cs b/src/Avalonia.Base/Media/ScaleTransform.cs index 259b23cbd2..d4c1a7f993 100644 --- a/src/Avalonia.Base/Media/ScaleTransform.cs +++ b/src/Avalonia.Base/Media/ScaleTransform.cs @@ -4,7 +4,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media { /// - /// Scale an . + /// Scale an . /// public class ScaleTransform : Transform { diff --git a/src/Avalonia.Base/Media/SkewTransform.cs b/src/Avalonia.Base/Media/SkewTransform.cs index a96710e331..066f5371c3 100644 --- a/src/Avalonia.Base/Media/SkewTransform.cs +++ b/src/Avalonia.Base/Media/SkewTransform.cs @@ -4,7 +4,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media { /// - /// Skews an . + /// Skews an . /// public class SkewTransform : Transform { diff --git a/src/Avalonia.Base/Media/Transform.cs b/src/Avalonia.Base/Media/Transform.cs index 023a8b9cdd..85393ab189 100644 --- a/src/Avalonia.Base/Media/Transform.cs +++ b/src/Avalonia.Base/Media/Transform.cs @@ -7,7 +7,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media { /// - /// Represents a transform on an . + /// Represents a transform on an . /// public abstract class Transform : Animatable, IMutableTransform { diff --git a/src/Avalonia.Base/Media/TranslateTransform.cs b/src/Avalonia.Base/Media/TranslateTransform.cs index d6d6809f3d..0f910f3600 100644 --- a/src/Avalonia.Base/Media/TranslateTransform.cs +++ b/src/Avalonia.Base/Media/TranslateTransform.cs @@ -4,7 +4,7 @@ using Avalonia.VisualTree; namespace Avalonia.Media { /// - /// Translates (moves) an . + /// Translates (moves) an . /// public class TranslateTransform : Transform { diff --git a/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs index 4eec214f4f..a991f2f657 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { /// - /// A node in the low-level scene graph representing an . + /// A node in the low-level scene graph representing an . /// internal class VisualNode : IVisualNode { From 8c019e669194784a29243a4922b6beb98dbf99e2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 30 Nov 2022 14:15:25 +0000 Subject: [PATCH 043/173] simpler fix. --- src/Avalonia.Controls/Window.cs | 47 ++------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 672099c3bb..280853967a 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -72,9 +72,6 @@ namespace Avalonia.Controls private bool _isExtendedIntoWindowDecorations; private Thickness _windowDecorationMargin; private Thickness _offScreenMargin; - private bool _shown; - private bool _showingAsDialog; - private bool _inShowHideMethods; /// /// Defines the property. @@ -179,6 +176,7 @@ namespace Avalonia.Controls private object? _dialogResult; private readonly Size _maxPlatformClientSize; private WindowStartupLocation _windowStartupLocation; + private bool _shown; /// /// Initializes static members of the class. @@ -509,14 +507,8 @@ namespace Avalonia.Controls } Owner = null; - _showingAsDialog = false; - _shown = false; - _inShowHideMethods = true; PlatformImpl?.Dispose(); - - _inShowHideMethods = false; - } private bool ShouldCancelClose(CancelEventArgs? args = null) @@ -594,10 +586,8 @@ namespace Avalonia.Controls Owner = null; PlatformImpl?.Hide(); - _shown = false; - _inShowHideMethods = true; IsVisible = false; - _inShowHideMethods = false; + _shown = false; } /// @@ -660,10 +650,8 @@ namespace Avalonia.Controls EnsureInitialized(); ApplyStyling(); - _inShowHideMethods = true; _shown = true; IsVisible = true; - _inShowHideMethods = false; var initialSize = new Size( double.IsNaN(Width) ? Math.Max(MinWidth, ClientSize.Width) : Width, @@ -743,11 +731,7 @@ namespace Avalonia.Controls EnsureInitialized(); ApplyStyling(); - _shown = true; - _showingAsDialog = true; - _inShowHideMethods = true; IsVisible = true; - _inShowHideMethods = false; var initialSize = new Size( double.IsNaN(Width) ? ClientSize.Width : Width, @@ -1018,33 +1002,6 @@ namespace Avalonia.Controls PlatformImpl?.SetSystemDecorations(typedNewValue); } - - if (change.Property == IsVisibleProperty) - { - if (!_inShowHideMethods) - { - var isVisible = change.GetNewValue(); - - if (_shown != isVisible) - { - if (!_shown) - { - ShowCore(null); - } - else - { - if (_showingAsDialog) - { - Close(false); - } - else - { - Hide(); - } - } - } - } - } } protected override AutomationPeer OnCreateAutomationPeer() From fb821c7899ca6ca1e900526df25d74326dddbb23 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 30 Nov 2022 14:26:34 +0000 Subject: [PATCH 044/173] ensure that isvisible = false can close dialogs. --- src/Avalonia.Controls/Window.cs | 230 +++++++++++++++++----------- src/Avalonia.Controls/WindowBase.cs | 18 +-- 2 files changed, 149 insertions(+), 99 deletions(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 280853967a..aa0ce50bad 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -177,6 +177,7 @@ namespace Avalonia.Controls private readonly Size _maxPlatformClientSize; private WindowStartupLocation _windowStartupLocation; private bool _shown; + private bool _showingAsDialog; /// /// Initializes static members of the class. @@ -509,6 +510,8 @@ namespace Avalonia.Controls Owner = null; PlatformImpl?.Dispose(); + + _showingAsDialog = false; } private bool ShouldCancelClose(CancelEventArgs? args = null) @@ -601,6 +604,33 @@ namespace Avalonia.Controls ShowCore(null); } + protected override void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e) + { + if (!IgnoreVisibilityChange) + { + var isVisible = e.GetNewValue(); + + if (_shown != isVisible) + { + if(!_shown) + { + Show(); + } + else + { + if (_showingAsDialog) + { + Close(false); + } + else + { + Hide(); + } + } + } + } + } + /// /// Shows the window as a child of . /// @@ -620,63 +650,72 @@ namespace Avalonia.Controls private void ShowCore(Window? parent) { - if (PlatformImpl == null) - { - throw new InvalidOperationException("Cannot re-show a closed window."); - } - - if (parent != null) + try { - if (parent.PlatformImpl == null) + IgnoreVisibilityChange = true; + + if (PlatformImpl == null) { - throw new InvalidOperationException("Cannot show a window with a closed parent."); + throw new InvalidOperationException("Cannot re-show a closed window."); } - else if (parent == this) + + if (parent != null) { - throw new InvalidOperationException("A Window cannot be its own parent."); + if (parent.PlatformImpl == null) + { + throw new InvalidOperationException("Cannot show a window with a closed parent."); + } + else if (parent == this) + { + throw new InvalidOperationException("A Window cannot be its own parent."); + } + else if (!parent.IsVisible) + { + throw new InvalidOperationException("Cannot show window with non-visible parent."); + } } - else if (!parent.IsVisible) + + if (_shown) { - throw new InvalidOperationException("Cannot show window with non-visible parent."); + return; } - } - if (_shown) - { - return; - } - - RaiseEvent(new RoutedEventArgs(WindowOpenedEvent)); + RaiseEvent(new RoutedEventArgs(WindowOpenedEvent)); - EnsureInitialized(); - ApplyStyling(); - _shown = true; - IsVisible = true; + EnsureInitialized(); + ApplyStyling(); + _shown = true; + IsVisible = true; - var initialSize = new Size( - double.IsNaN(Width) ? Math.Max(MinWidth, ClientSize.Width) : Width, - double.IsNaN(Height) ? Math.Max(MinHeight, ClientSize.Height) : Height); + var initialSize = new Size( + double.IsNaN(Width) ? Math.Max(MinWidth, ClientSize.Width) : Width, + double.IsNaN(Height) ? Math.Max(MinHeight, ClientSize.Height) : Height); - if (initialSize != ClientSize) - { - PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout); - } + if (initialSize != ClientSize) + { + PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout); + } - LayoutManager.ExecuteInitialLayoutPass(); + LayoutManager.ExecuteInitialLayoutPass(); - if (PlatformImpl != null && parent?.PlatformImpl is not null) - { - PlatformImpl.SetParent(parent.PlatformImpl); - } + if (PlatformImpl != null && parent?.PlatformImpl is not null) + { + PlatformImpl.SetParent(parent.PlatformImpl); + } - Owner = parent; - parent?.AddChild(this, false); + Owner = parent; + parent?.AddChild(this, false); - SetWindowStartupLocation(parent?.PlatformImpl); + SetWindowStartupLocation(parent?.PlatformImpl); - PlatformImpl?.Show(ShowActivated, false); - Renderer?.Start(); - OnOpened(EventArgs.Empty); + PlatformImpl?.Show(ShowActivated, false); + Renderer?.Start(); + OnOpened(EventArgs.Empty); + } + finally + { + IgnoreVisibilityChange = false; + } } /// @@ -706,68 +745,79 @@ namespace Avalonia.Controls /// public Task ShowDialog(Window owner) { - if (owner == null) - { - throw new ArgumentNullException(nameof(owner)); - } - else if (owner.PlatformImpl == null) - { - throw new InvalidOperationException("Cannot show a window with a closed owner."); - } - else if (owner == this) - { - throw new InvalidOperationException("A Window cannot be its own owner."); - } - else if (IsVisible) - { - throw new InvalidOperationException("The window is already being shown."); - } - else if (!owner.IsVisible) + try { - throw new InvalidOperationException("Cannot show window with non-visible parent."); - } + IgnoreVisibilityChange = true; + + if (owner == null) + { + throw new ArgumentNullException(nameof(owner)); + } + else if (owner.PlatformImpl == null) + { + throw new InvalidOperationException("Cannot show a window with a closed owner."); + } + else if (owner == this) + { + throw new InvalidOperationException("A Window cannot be its own owner."); + } + else if (IsVisible) + { + throw new InvalidOperationException("The window is already being shown."); + } + else if (!owner.IsVisible) + { + throw new InvalidOperationException("Cannot show window with non-visible parent."); + } - RaiseEvent(new RoutedEventArgs(WindowOpenedEvent)); + RaiseEvent(new RoutedEventArgs(WindowOpenedEvent)); - EnsureInitialized(); - ApplyStyling(); - IsVisible = true; + EnsureInitialized(); + ApplyStyling(); + _shown = true; + _showingAsDialog = true; + IsVisible = true; - var initialSize = new Size( - double.IsNaN(Width) ? ClientSize.Width : Width, - double.IsNaN(Height) ? ClientSize.Height : Height); + var initialSize = new Size( + double.IsNaN(Width) ? ClientSize.Width : Width, + double.IsNaN(Height) ? ClientSize.Height : Height); - if (initialSize != ClientSize) - { - PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout); - } + if (initialSize != ClientSize) + { + PlatformImpl?.Resize(initialSize, PlatformResizeReason.Layout); + } - LayoutManager.ExecuteInitialLayoutPass(); + LayoutManager.ExecuteInitialLayoutPass(); - var result = new TaskCompletionSource(); + var result = new TaskCompletionSource(); - PlatformImpl?.SetParent(owner.PlatformImpl); - Owner = owner; - owner.AddChild(this, true); + PlatformImpl?.SetParent(owner.PlatformImpl); + Owner = owner; + owner.AddChild(this, true); - SetWindowStartupLocation(owner.PlatformImpl); + SetWindowStartupLocation(owner.PlatformImpl); - PlatformImpl?.Show(ShowActivated, true); + PlatformImpl?.Show(ShowActivated, true); - Renderer?.Start(); + Renderer?.Start(); - Observable.FromEventPattern( - x => Closed += x, - x => Closed -= x) - .Take(1) - .Subscribe(_ => - { - owner.Activate(); - result.SetResult((TResult)(_dialogResult ?? default(TResult)!)); - }); + Observable.FromEventPattern( + x => Closed += x, + x => Closed -= x) + .Take(1) + .Subscribe(_ => + { + owner.Activate(); + result.SetResult((TResult)(_dialogResult ?? default(TResult)!)); + }); - OnOpened(EventArgs.Empty); - return result.Task; + OnOpened(EventArgs.Empty); + return result.Task; + } + finally + { + IgnoreVisibilityChange = false; + } } private void UpdateEnabled() diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 8f1b2198ad..46653c8203 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -42,7 +42,7 @@ namespace Avalonia.Controls private bool _hasExecutedInitialLayoutPass; private bool _isActive; - private bool _ignoreVisibilityChange; + protected bool IgnoreVisibilityChange { get; set; } private WindowBase? _owner; static WindowBase() @@ -125,7 +125,7 @@ namespace Avalonia.Controls /// public virtual void Hide() { - _ignoreVisibilityChange = true; + IgnoreVisibilityChange = true; try { @@ -135,7 +135,7 @@ namespace Avalonia.Controls } finally { - _ignoreVisibilityChange = false; + IgnoreVisibilityChange = false; } } @@ -144,7 +144,7 @@ namespace Avalonia.Controls /// public virtual void Show() { - _ignoreVisibilityChange = true; + IgnoreVisibilityChange = true; try { @@ -163,7 +163,7 @@ namespace Avalonia.Controls } finally { - _ignoreVisibilityChange = false; + IgnoreVisibilityChange = false; } } @@ -202,7 +202,7 @@ namespace Avalonia.Controls protected override void HandleClosed() { - _ignoreVisibilityChange = true; + IgnoreVisibilityChange = true; try { @@ -217,7 +217,7 @@ namespace Avalonia.Controls } finally { - _ignoreVisibilityChange = false; + IgnoreVisibilityChange = false; } } @@ -318,9 +318,9 @@ namespace Avalonia.Controls Deactivated?.Invoke(this, EventArgs.Empty); } - private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e) + protected virtual void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e) { - if (!_ignoreVisibilityChange) + if (!IgnoreVisibilityChange) { if ((bool)e.NewValue!) { From 4a490694a24f7e0cd2eff83cd7832fb2f18655f9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 30 Nov 2022 20:08:42 +0000 Subject: [PATCH 045/173] fix unit test. --- src/Avalonia.Controls/Window.cs | 43 ++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index aa0ce50bad..1ff08f0169 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -567,30 +567,39 @@ namespace Avalonia.Controls /// public override void Hide() { - if (!_shown) + try { - return; - } + IgnoreVisibilityChange = true; + + if (!_shown) + { + return; + } - Renderer?.Stop(); + Renderer?.Stop(); - if (Owner is Window owner) - { - owner.RemoveChild(this); - } + if (Owner is Window owner) + { + owner.RemoveChild(this); + } - if (_children.Count > 0) - { - foreach (var child in _children.ToArray()) + if (_children.Count > 0) { - child.child.Hide(); + foreach (var child in _children.ToArray()) + { + child.child.Hide(); + } } - } - Owner = null; - PlatformImpl?.Hide(); - IsVisible = false; - _shown = false; + Owner = null; + PlatformImpl?.Hide(); + IsVisible = false; + _shown = false; + } + finally + { + IgnoreVisibilityChange = false; + } } /// From a2ba58c0f95759f4ee4644b5b2c9c74eef219d52 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 1 Dec 2022 10:53:23 +0600 Subject: [PATCH 046/173] Added some tests for composition animations --- .../Animations/KeyFrameAnimationInstance.cs | 7 +- .../Composition/CompositionAnimationTests.cs | 104 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs index 570c6a6d07..70462dd37b 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs @@ -130,7 +130,12 @@ namespace Avalonia.Rendering.Composition.Animations right = _keyFrames[c + 1]; } else if (c == 0) - return ExpressionVariant.Create(GetKeyFrame(ref ctx, kf)); + { + // The current progress is before the first frame, we implicitly use the starting value + // as the first frame in this case + right = _keyFrames[c]; + break; + } else break; } diff --git a/tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs b/tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs new file mode 100644 index 0000000000..9b38422dde --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Avalonia.Animation.Easings; +using Avalonia.Base.UnitTests.Rendering; +using Avalonia.Rendering; +using Avalonia.Rendering.Composition; +using Avalonia.Rendering.Composition.Expressions; +using Avalonia.Rendering.Composition.Server; +using Avalonia.Threading; +using Xunit; +using Xunit.Sdk; + +namespace Avalonia.Base.UnitTests.Composition; + +public class CompositionAnimationTests +{ + + class AnimationDataProvider : DataAttribute + { + IEnumerable Generate() => + new AnimationData[] + { + new("3 frames starting from 0") + { + Frames = + { + (0f, 10f), + (0.5f, 30f), + (1f, 20f) + }, + Checks = + { + (0.25f, 20f), + (0.5f, 30f), + (0.75f, 25f), + (1f, 20f) + } + }, + new("1 final frame") + { + Frames = + { + (1f, 10f) + }, + Checks = + { + (0f, 0f), + (0.5f, 5f), + (1f, 10f) + } + } + }; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (var ani in Generate()) + { + yield return new Object[] { ani }; + } + } + } + + [AnimationDataProvider] + [Theory] + public void GenericCheck(AnimationData data) + { + var compositor = + new Compositor(new RenderLoop(new CompositorTestsBase.ManualRenderTimer(), new Dispatcher(null)), null); + var target = compositor.CreateSolidColorVisual(); + var ani = new ScalarKeyFrameAnimation(null); + foreach (var frame in data.Frames) + ani.InsertKeyFrame(frame.key, frame.value, new LinearEasing()); + ani.Duration = TimeSpan.FromSeconds(1); + var instance = ani.CreateInstance(target.Server, null); + instance.Initialize(TimeSpan.Zero, data.StartingValue, ServerCompositionVisual.s_IdOfRotationAngleProperty); + var currentValue = ExpressionVariant.Create(data.StartingValue); + foreach (var check in data.Checks) + { + currentValue = instance.Evaluate(TimeSpan.FromSeconds(check.time), currentValue); + Assert.Equal(check.value, currentValue.Scalar); + } + + } + + public class AnimationData + { + public AnimationData(string name) + { + Name = name; + } + + public string Name { get; } + public List<(float key, float value)> Frames { get; set; } = new(); + public List<(float time, float value)> Checks { get; set; } = new(); + public float StartingValue { get; set; } + public float Duration { get; set; } = 1; + public override string ToString() + { + return Name; + } + } +} \ No newline at end of file From 390221b3cee6173161e620da648081b477de9206 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 22:32:27 -0500 Subject: [PATCH 047/173] Implement compile time MergedDictionaties --- .../Themes/Fluent/ColorPicker.xaml | 4 - .../Themes/Fluent/Fluent.xaml | 10 +- .../Themes/Simple/ColorPicker.xaml | 4 - .../Themes/Simple/Simple.xaml | 10 +- src/Avalonia.Themes.Fluent/Accents/Base.xaml | 1 + .../Controls/Button.xaml | 2 - .../Controls/FluentControls.xaml | 126 +++++++++--------- .../Controls/RepeatButton.xaml | 2 - .../Controls/ToggleButton.xaml | 2 - src/Avalonia.Themes.Fluent/FluentTheme.xaml | 4 +- .../FluentTheme.xaml.cs | 8 +- .../Controls/SimpleControls.xaml | 124 ++++++++--------- src/Avalonia.Themes.Simple/SimpleTheme.xaml | 2 +- .../SimpleTheme.xaml.cs | 4 +- .../AvaloniaRuntimeXamlLoader.cs | 4 +- .../AvaloniaXamlIlCompiler.cs | 1 + .../IXamlAstGroupTransformer.cs | 2 +- .../XamlIncludeGroupTransformer.cs | 75 +++++++---- .../XamlMergeResourceGroupTransformer.cs | 113 ++++++++++++++++ .../AvaloniaXamlIlWellKnownTypes.cs | 2 + .../XamlDocumentParseException.cs | 5 + .../Avalonia.Markup.Xaml.csproj | 1 + .../Styling/MergeResourceInclude.cs | 69 ++++++++++ .../Xaml/MergeResourceIncludeTests.cs | 124 +++++++++++++++++ 24 files changed, 515 insertions(+), 184 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs create mode 100644 src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml index 9d2a994e5b..2ccf20d460 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml @@ -3,10 +3,6 @@ xmlns:controls="using:Avalonia.Controls" xmlns:primitives="using:Avalonia.Controls.Primitives"> - - - - diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml index 85f4e417e6..2cc8a1d38a 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml @@ -50,13 +50,13 @@ - - - + + + - - + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml index 626ddd4b43..b82d36a288 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml @@ -3,10 +3,6 @@ xmlns:controls="using:Avalonia.Controls" xmlns:primitives="using:Avalonia.Controls.Primitives"> - - - - diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml index 7aefa23706..8c4dfa9c87 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml @@ -50,13 +50,13 @@ - - - + + + - - + + diff --git a/src/Avalonia.Themes.Fluent/Accents/Base.xaml b/src/Avalonia.Themes.Fluent/Accents/Base.xaml index 259d107b5c..479bcd8531 100644 --- a/src/Avalonia.Themes.Fluent/Accents/Base.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/Base.xaml @@ -22,6 +22,7 @@ 10,6,6,5 20 20 + 8,5,8,6 3 diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index 7828fd52ed..126f2c22e0 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -8,8 +8,6 @@ - - 8,5,8,6 diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index a029be6b8d..2733365479 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -4,70 +4,70 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml index a54187104b..fd04c85fed 100644 --- a/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml @@ -10,8 +10,6 @@ - 8,5,8,6 - diff --git a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml index 7a46f21534..da2021790a 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml @@ -10,8 +10,6 @@ - 8,5,8,6 - diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml index d8f8267fe5..44ca60e2fa 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml @@ -4,8 +4,8 @@ - - + + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs index 728e81b198..80460e1bde 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs @@ -97,15 +97,15 @@ namespace Avalonia.Themes.Fluent var themeVariantResource1 = Mode == FluentThemeMode.Dark ? _baseDark : _baseLight; var themeVariantResource2 = Mode == FluentThemeMode.Dark ? _fluentDark : _fluentLight; var dict = Resources.MergedDictionaries; - if (dict.Count == 2) + if (dict.Count == 0) { - dict.Insert(1, themeVariantResource1); + dict.Add(themeVariantResource1); dict.Add(themeVariantResource2); } else { - dict[1] = themeVariantResource1; - dict[3] = themeVariantResource2; + dict[0] = themeVariantResource1; + dict[1] = themeVariantResource2; } } diff --git a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml index 4aefa0136c..2d7fdcdd50 100644 --- a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml +++ b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml @@ -3,68 +3,68 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml b/src/Avalonia.Themes.Simple/SimpleTheme.xaml index fe296bd288..5b0cae7fd2 100644 --- a/src/Avalonia.Themes.Simple/SimpleTheme.xaml +++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml @@ -4,7 +4,7 @@ - + diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs index af9d305043..e452646eab 100644 --- a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs +++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs @@ -56,13 +56,13 @@ namespace Avalonia.Themes.Simple { var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight; var dict = Resources.MergedDictionaries; - if (dict.Count == 1) + if (dict.Count == 0) { dict.Add(themeVariantResource); } else { - dict[1] = themeVariantResource; + dict[0] = themeVariantResource; } } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs index b4c951fc5e..6f6420f66d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs @@ -56,8 +56,8 @@ namespace Avalonia.Markup.Xaml /// /// Collection of documents. /// Xaml loader configuration. - /// The loaded objects per each input document. - public static IReadOnlyList LoadGroup(IReadOnlyCollection documents, RuntimeXamlLoaderConfiguration? configuration = null) + /// The loaded objects per each input document. If document was removed, the element by index is null. + public static IReadOnlyList LoadGroup(IReadOnlyCollection documents, RuntimeXamlLoaderConfiguration? configuration = null) => AvaloniaXamlIlRuntimeCompiler.LoadGroup(documents, configuration ?? new RuntimeXamlLoaderConfiguration()); /// diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index b8350c3f11..aaaee39b0d 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -85,6 +85,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions GroupTransformers = new() { + new XamlMergeResourceGroupTransformer(), new AvaloniaXamlIncludeTransformer() }; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs index 32bf37431f..eeb5293325 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs @@ -18,7 +18,7 @@ internal class AstGroupTransformationContext : AstTransformationContext public IXamlDocumentResource CurrentDocument { get; set; } public IReadOnlyCollection Documents { get; } - + public new IXamlAstNode ParseError(string message, IXamlAstNode node) => Error(node, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, node)); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs index cc29d5ccb5..813c135dc6 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs @@ -15,6 +15,7 @@ using XamlX.TypeSystem; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers; +#nullable enable internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer { public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node) @@ -34,40 +35,26 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer { throw new InvalidOperationException($"\"{nodeTypeName}\".Loaded property is expected to be defined"); } - + if (valueNode.Manipulation is not XamlObjectInitializationNode { Manipulation: XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty }) { - return context.ParseError($"Source property must be set on the \"{nodeTypeName}\" node.", node); + throw new XamlDocumentParseException(context.CurrentDocument, + $"Source property must be set on the \"{nodeTypeName}\" node.", valueNode); } - // We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`. - if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode - || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri - || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath } - || sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind }) + var (assetPathUri, sourceUriNode) = ResolveSourceFromXamlInclude(context, nodeTypeName, sourceProperty, false); + if (assetPathUri is null) { - // TODO: make it a compiler warning - // Source value can be set with markup extension instead of the Uri object node, we don't support it here yet. return node; } - - var uriPath = new Uri(originalAssetPath, (UriKind)uriKind); - if (!uriPath.IsAbsoluteUri) + else { - var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null."); - uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath); - } - else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase)) - { - return context.ParseError( - $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", - sourceUriNode, node); + sourceUriNode ??= valueNode; } - var assetPathUri = Uri.UnescapeDataString(uriPath.AbsoluteUri); var assetPath = assetPathUri.Replace("avares://", ""); var assemblyNameSeparator = assetPath.IndexOf('/'); var assembly = assetPath.Substring(0, assemblyNameSeparator); @@ -119,7 +106,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer $"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly.", sourceUriNode, node); } - + private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li, IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly) { @@ -151,7 +138,49 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer new[] { new NewServiceProviderNode(sp, li) }); } - internal class NewServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack, + internal static (string?, IXamlAstNode?) ResolveSourceFromXamlInclude( + AstGroupTransformationContext context, string nodeTypeName, XamlPropertyAssignmentNode sourceProperty, + bool strictSourceValueType) + { + // We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`. + if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceUriNode + || sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri + || sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath } + || sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind }) + { + // Source value can be set with markup extension instead of the Uri object node, we don't support it here yet. + var anyPropValue = sourceProperty.Values.FirstOrDefault(); + if (strictSourceValueType) + { + context.Error(anyPropValue, + new XamlDocumentParseException(context.CurrentDocument, + $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", anyPropValue)); + } + else + { + // TODO: make it a compiler warning + } + return (null, anyPropValue); + } + + var uriPath = new Uri(originalAssetPath, (UriKind)uriKind); + if (!uriPath.IsAbsoluteUri) + { + var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null."); + uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath); + } + else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase)) + { + context.Error(sourceUriNode, + new XamlDocumentParseException(context.CurrentDocument, + $"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", sourceUriNode)); + return (null, sourceUriNode); + } + + return (Uri.UnescapeDataString(uriPath.AbsoluteUri), sourceUriNode); + } + + private class NewServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack, IXamlAstEmitableNode { public NewServiceProviderNode(IXamlType type, IXamlLineInfo lineInfo) : base(lineInfo) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs new file mode 100644 index 0000000000..8c83c74248 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX.Ast; +using XamlX.IL.Emitters; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers; +#nullable enable + +internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer +{ + public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node) + { + var resourceDictionaryType = context.GetAvaloniaTypes().ResourceDictionary; + if (node is not XamlObjectInitializationNode resourceDictionaryNode + || resourceDictionaryNode.Type != resourceDictionaryType + || resourceDictionaryNode.Manipulation is not XamlManipulationGroupNode resourceDictionaryManipulation) + { + return node; + } + + var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude; + var mergeSourceNodes = new List(); + var hasAnyNonMergedResource = false; + foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray()) + { + void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode) + { + if (assignmentNode.Property.Name == "MergedDictionaries" + && assignmentNode.Values.FirstOrDefault() is XamlValueWithManipulationNode valueNode) + { + if (valueNode.Type.GetClrType() == mergeResourceIncludeType) + { + if (valueNode.Manipulation is XamlObjectInitializationNode objectInitialization + && objectInitialization.Manipulation is XamlPropertyAssignmentNode sourceAssignmentNode) + { + parent.Children.Remove(assignmentNode); + mergeSourceNodes.Add(sourceAssignmentNode); + } + else + { + throw new XamlDocumentParseException(context.CurrentDocument, + "Invalid MergeResourceInclude node found. Make sure that Source property is set.", + valueNode); + } + } + else + { + hasAnyNonMergedResource = true; + } + + if (hasAnyNonMergedResource && mergeSourceNodes.Any()) + { + throw new XamlDocumentParseException(context.CurrentDocument, + "Mix of MergeResourceInclude and other dictionaries inside of the ResourceDictionary.MergedDictionaries is not allowed", + valueNode); + } + } + } + + if (manipulationNode is XamlPropertyAssignmentNode singleValueAssignment) + { + ProcessXamlPropertyAssignmentNode(resourceDictionaryManipulation, singleValueAssignment); + } + else if (manipulationNode is XamlManipulationGroupNode groupNodeValues) + { + foreach (var groupNodeValue in groupNodeValues.Children.OfType().ToArray()) + { + ProcessXamlPropertyAssignmentNode(groupNodeValues, groupNodeValue); + } + } + } + + var manipulationGroup = new XamlManipulationGroupNode(node, new List()); + foreach (var sourceNode in mergeSourceNodes) + { + var (originalAssetPath, propertyNode) = + AvaloniaXamlIncludeTransformer.ResolveSourceFromXamlInclude(context, "MergeResourceInclude", sourceNode, true); + if (originalAssetPath is null) + { + return node; + } + + var targetDocument = context.Documents.FirstOrDefault(d => + string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase)) + ?.XamlDocument.Root as XamlValueWithManipulationNode; + if (targetDocument is null) + { + return context.ParseError( + $"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node); + } + + var singleRootObject = ((XamlManipulationGroupNode)targetDocument.Manipulation) + .Children.OfType().Single(); + if (singleRootObject.Type != resourceDictionaryType) + { + return context.ParseError( + $"MergeResourceInclude can only include another ResourceDictionary", propertyNode, node); + } + + manipulationGroup.Children.Add(singleRootObject.Manipulation); + } + + if (manipulationGroup.Children.Any()) + { + // MergedDictionaries are read first, so we need ot inject our merged values in the beginning. + resourceDictionaryManipulation.Children.Insert(0, manipulationGroup); + } + + return node; + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 87a037c16a..5753a1008c 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -104,6 +104,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType IStyle { get; } public IXamlType StyleInclude { get; } public IXamlType ResourceInclude { get; } + public IXamlType MergeResourceInclude { get; } public IXamlType IResourceDictionary { get; } public IXamlType ResourceDictionary { get; } public IXamlMethod ResourceDictionaryDeferredAdd { get; } @@ -236,6 +237,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle"); StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude"); ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude"); + MergeResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.MergeResourceInclude"); IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary"); ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary"); ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object, diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs index d031a6086b..0532287a67 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs @@ -18,4 +18,9 @@ internal class XamlDocumentParseException : XamlParseException { FilePath = path; } + + public XamlDocumentParseException(IXamlDocumentResource document, string message, IXamlLineInfo lineInfo) + : this(document.FileSource?.FilePath, message, lineInfo) + { + } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 0ab00007e7..4c3aaa4ec0 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -44,6 +44,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs new file mode 100644 index 0000000000..c81a3c1416 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs @@ -0,0 +1,69 @@ +using System; +using Avalonia.Controls; + +namespace Avalonia.Markup.Xaml.Styling; + +public class MergeResourceInclude : IResourceProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly Uri? _baseUri; + private IResourceDictionary? _loaded; + private bool _isLoading; + + /// + /// Initializes a new instance of the class. + /// + /// The XAML service provider. + public MergeResourceInclude(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _baseUri = serviceProvider.GetContextBaseUri(); + } + + /// + /// Gets the loaded resource dictionary. + /// + public IResourceDictionary Loaded + { + get + { + if (_loaded == null) + { + _isLoading = true; + _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(_serviceProvider, Source, _baseUri); + _isLoading = false; + } + + return _loaded; + } + } + + public IResourceHost? Owner => Loaded.Owner; + + /// + /// Gets or sets the source URL. + /// + public Uri? Source { get; set; } + + bool IResourceNode.HasResources => Loaded.HasResources; + + public event EventHandler? OwnerChanged + { + add => Loaded.OwnerChanged += value; + remove => Loaded.OwnerChanged -= value; + } + + bool IResourceNode.TryGetResource(object key, out object? value) + { + if (!_isLoading) + { + return Loaded.TryGetResource(key, out value); + } + + value = null; + return false; + } + + void IResourceProvider.AddOwner(IResourceHost owner) => Loaded.AddOwner(owner); + void IResourceProvider.RemoveOwner(IResourceHost owner) => Loaded.RemoveOwner(owner); +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs new file mode 100644 index 0000000000..520abee59a --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs @@ -0,0 +1,124 @@ +using System; +using System.Xml; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Styling; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml; + +public class MergeResourceIncludeTests +{ + [Fact] + public void MergeResourceInclude_Works_With_Single_Resource() + { + var documents = new[] + { + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources.xaml"), @" + + Red +"), + new RuntimeXamlLoaderDocument(@" + + + + Blue + + + + + +") + }; + + var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents); + var contentControl = Assert.IsType(objects[1]); + + var resources = Assert.IsType(contentControl.Resources); + Assert.Empty(resources.MergedDictionaries); + + var initialResource = (ISolidColorBrush)resources["brush1"]!; + Assert.Equal(Colors.Blue, initialResource.Color); + + var mergedResource = (ISolidColorBrush)resources["brush2"]!; + Assert.Equal(Colors.Red, mergedResource.Color); + } + + [Fact] + public void Mixing_MergeResourceInclude_And_ResourceInclude_Is_Not_Allowed() + { + var documents = new[] + { + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1.xaml"), @" + + Red +"), + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources2.xaml"), @" + + Blue +"), + new RuntimeXamlLoaderDocument(@" + + + + + +") + }; + + Assert.ThrowsAny(() => AvaloniaRuntimeXamlLoader.LoadGroup(documents)); + } + + [Fact] + public void MergeResourceInclude_Works_With_Multiple_Resources() + { + var documents = new[] + { + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1.xaml"), @" + + Red + Blue +"), + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources2.xaml"), @" + + Yellow + + + +"), + new RuntimeXamlLoaderDocument(@" + + + + + + Black + White +"), + new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1_2.xaml"), @" + + Green +"), + }; + + var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents); + var resources = Assert.IsType(objects[2]); + Assert.Empty(resources.MergedDictionaries); + + Assert.Equal(Colors.Red, ((ISolidColorBrush)resources["brush1"]!).Color); + Assert.Equal(Colors.Blue, ((ISolidColorBrush)resources["brush2"]!).Color); + Assert.Equal(Colors.Green, ((ISolidColorBrush)resources["brush3"]!).Color); + Assert.Equal(Colors.Yellow, ((ISolidColorBrush)resources["brush4"]!).Color); + Assert.Equal(Colors.Black, ((ISolidColorBrush)resources["brush5"]!).Color); + Assert.Equal(Colors.White, ((ISolidColorBrush)resources["brush6"]!).Color); + } +} From c03187ff7ce32d8b64b5b823bf16382d6ed6f013 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 1 Dec 2022 01:14:13 -0500 Subject: [PATCH 048/173] Add a new benchmark --- .../Themes/ThemeBenchmark.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs index e82576c7d9..70636d1fe6 100644 --- a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs @@ -16,6 +16,8 @@ namespace Avalonia.Benchmarks.Themes public class ThemeBenchmark : IDisposable { private IDisposable _app; + private readonly FluentTheme _reusableFluentTheme = new FluentTheme(); + private readonly SimpleTheme _reusableSimpleTheme = new SimpleTheme(); public ThemeBenchmark() { @@ -49,6 +51,26 @@ namespace Avalonia.Benchmarks.Themes }; return ((IResourceHost)UnitTestApplication.Current).TryGetResource("ThemeAccentColor", out _); } + + [Benchmark] + [Arguments(typeof(Button))] + [Arguments(typeof(TextBox))] + [Arguments(typeof(DatePicker))] + public object FindFluentControlTheme(Type type) + { + _reusableFluentTheme.TryGetResource(type, out var theme); + return theme; + } + + [Benchmark] + [Arguments(typeof(Button))] + [Arguments(typeof(TextBox))] + [Arguments(typeof(DatePicker))] + public object FindSimpleControlTheme(Type type) + { + _reusableSimpleTheme.TryGetResource(type, out var theme); + return theme; + } public void Dispose() { From de9e9580c00a0310e95d459ec4ad4b40e30fe9ad Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 25 Nov 2022 21:15:20 -0500 Subject: [PATCH 049/173] Pass parent's service provider to the StyleInclude, so parents stack is complete --- .../BuildEngineErrorCode.cs | 1 + .../XamlCompilerTaskExecutor.cs | 119 ++++++++++++++---- .../FluentTheme.xaml.cs | 8 +- .../SimpleTheme.xaml.cs | 8 +- .../XamlIncludeGroupTransformer.cs | 3 +- ...IlConstructorServiceProviderTransformer.cs | 12 ++ .../AvaloniaXamlLoader.cs | 41 +++++- .../StaticResourceExtension.cs | 21 +--- .../Styling/ResourceInclude.cs | 4 +- .../Styling/StyleInclude.cs | 4 +- .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 28 +++-- .../Avalonia.Markup.Xaml.UnitTests.csproj | 1 + .../Xaml/StyleIncludeTests.cs | 43 +++++++ .../Xaml/StyleWithServiceLocator.xaml | 5 + .../Xaml/StyleWithServiceLocator.xaml.cs | 16 +++ 15 files changed, 251 insertions(+), 63 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs diff --git a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs index a149a758f4..a31c9a7516 100644 --- a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs +++ b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs @@ -7,6 +7,7 @@ namespace Avalonia.Build.Tasks LegacyResmScheme = 3, TransformError = 4, EmitError = 4, + Loader = 5, Unknown = 9999 } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index ea2cf2cf99..a394d47904 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -196,7 +196,8 @@ namespace Avalonia.Build.Tasks var runtimeHelpers = typeSystem.GetType("Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers"); var createRootServiceProviderMethod = asm.MainModule.ImportReference( typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods - .First(x => x.Name == "CreateRootServiceProviderV2")); + .First(x => x.Name == "CreateRootServiceProviderV3")); + var serviceProviderType = createRootServiceProviderMethod.ReturnType; var loaderDispatcherDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!XamlLoader", TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object); @@ -212,11 +213,36 @@ namespace Avalonia.Build.Tasks MethodAttributes.Static | MethodAttributes.Public, asm.MainModule.TypeSystem.Object) { - Parameters = {new ParameterDefinition(asm.MainModule.TypeSystem.String)} + Parameters = + { + new ParameterDefinition(serviceProviderType), + new ParameterDefinition(asm.MainModule.TypeSystem.String) + }, + }; + var loaderDispatcherMethodOld = new MethodDefinition("TryLoad", + MethodAttributes.Static | MethodAttributes.Public, + asm.MainModule.TypeSystem.Object) + { + Parameters = + { + new ParameterDefinition(asm.MainModule.TypeSystem.String) + }, + Body = + { + Instructions = + { + Instruction.Create(OpCodes.Ldnull), + Instruction.Create(OpCodes.Ldarg_0), + Instruction.Create(OpCodes.Call, loaderDispatcherMethod), + Instruction.Create(OpCodes.Ret) + } + } }; loaderDispatcherDef.Methods.Add(loaderDispatcherMethod); + loaderDispatcherDef.Methods.Add(loaderDispatcherMethodOld); asm.MainModule.Types.Add(loaderDispatcherDef); + var stringEquals = asm.MainModule.ImportReference(asm.MainModule.TypeSystem.String.Resolve().Methods.First( m => m.IsStatic && m.Name == "Equals" && m.Parameters.Count == 3 && @@ -377,30 +403,42 @@ namespace Avalonia.Build.Tasks classTypeDefinition.Fields.Add(designLoaderField); const string TrampolineName = "!XamlIlPopulateTrampoline"; - var trampoline = new MethodDefinition(TrampolineName, - MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void); - trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition)); - classTypeDefinition.Methods.Add(trampoline); + var trampolineMethodWithoutSP = new Lazy(() => CreateTrampolineMethod(false)); + var trampolineMethodWithSP = new Lazy(() => CreateTrampolineMethod(true)); + MethodDefinition CreateTrampolineMethod(bool hasSystemProviderArg) + { + var trampoline = new MethodDefinition(TrampolineName, + MethodAttributes.Static | MethodAttributes.Private, asm.MainModule.TypeSystem.Void); + if (hasSystemProviderArg) + { + trampoline.Parameters.Add(new ParameterDefinition(serviceProviderType)); + } + trampoline.Parameters.Add(new ParameterDefinition(classTypeDefinition)); + + classTypeDefinition.Methods.Add(trampoline); - var regularStart = Instruction.Create(OpCodes.Call, createRootServiceProviderMethod); + var regularStart = Instruction.Create(OpCodes.Nop); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, regularStart)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, designLoaderField)); + trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, designLoaderLoad)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - trampoline.Body.Instructions.Add(regularStart); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod)); - trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - CopyDebugDocument(trampoline, compiledPopulateMethod); + trampoline.Body.Instructions.Add(regularStart); + trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_0 : OpCodes.Ldnull)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); + trampoline.Body.Instructions.Add(Instruction.Create(hasSystemProviderArg ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Call, compiledPopulateMethod)); + trampoline.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + CopyDebugDocument(trampoline, compiledPopulateMethod); + return trampoline; + } var foundXamlLoader = false; - // Find AvaloniaXamlLoader.Load(this) and replace it with !XamlIlPopulateTrampoline(this) - foreach (var method in classTypeDefinition.Methods - .Where(m => !m.Attributes.HasFlag(MethodAttributes.Static))) + // Find AvaloniaXamlLoader.Load(this) or AvaloniaXamlLoader.Load(sp, this) and replace it with !XamlIlPopulateTrampoline(this) + foreach (var method in classTypeDefinition.Methods.ToArray()) { var i = method.Body.Instructions; for (var c = 1; c < i.Count; c++) @@ -422,7 +460,20 @@ namespace Avalonia.Build.Tasks { if (MatchThisCall(i, c - 1)) { - i[c].Operand = trampoline; + i[c].Operand = trampolineMethodWithoutSP.Value; + foundXamlLoader = true; + } + } + if (op != null + && op.Name == "Load" + && op.Parameters.Count == 2 + && op.Parameters[0].ParameterType.FullName == "System.IServiceProvider" + && op.Parameters[1].ParameterType.FullName == "System.Object" + && op.DeclaringType.FullName == "Avalonia.Markup.Xaml.AvaloniaXamlLoader") + { + if (MatchThisCall(i, c - 1)) + { + i[c].Operand = trampolineMethodWithSP.Value; foundXamlLoader = true; } } @@ -439,7 +490,7 @@ namespace Avalonia.Build.Tasks { var i = ctors[0].Body.Instructions; var retIdx = i.IndexOf(i.Last(x => x.OpCode == OpCodes.Ret)); - i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampoline)); + i.Insert(retIdx, Instruction.Create(OpCodes.Call, trampolineMethodWithoutSP.Value)); i.Insert(retIdx, Instruction.Create(OpCodes.Ldarg_0)); } else @@ -461,20 +512,33 @@ namespace Avalonia.Build.Tasks null : classTypeDefinition.GetConstructors().FirstOrDefault(c => c.IsPublic && !c.IsStatic && !c.HasParameters); + var constructorWithSp = compiledBuildMethod != null ? + null : + classTypeDefinition.GetConstructors().FirstOrDefault(c => + c.IsPublic && !c.IsStatic && c.Parameters.Count == 1 && c.Parameters[0].ParameterType.FullName == serviceProviderType.FullName); - if (compiledBuildMethod != null || parameterlessConstructor != null) + if (compiledBuildMethod != null || parameterlessConstructor != null || constructorWithSp != null) { var i = loaderDispatcherMethod.Body.Instructions; var nop = Instruction.Create(OpCodes.Nop); - i.Add(Instruction.Create(OpCodes.Ldarg_0)); + i.Add(Instruction.Create(OpCodes.Ldarg_1)); i.Add(Instruction.Create(OpCodes.Ldstr, res.Uri)); i.Add(Instruction.Create(OpCodes.Ldc_I4, (int)StringComparison.OrdinalIgnoreCase)); i.Add(Instruction.Create(OpCodes.Call, stringEquals)); i.Add(Instruction.Create(OpCodes.Brfalse, nop)); if (parameterlessConstructor != null) + { i.Add(Instruction.Create(OpCodes.Newobj, parameterlessConstructor)); + } + else if (constructorWithSp != null) + { + i.Add(Instruction.Create(OpCodes.Ldarg_0)); + i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); + i.Add(Instruction.Create(OpCodes.Newobj, constructorWithSp)); + } else { + i.Add(Instruction.Create(OpCodes.Ldarg_0)); i.Add(Instruction.Create(OpCodes.Call, createRootServiceProviderMethod)); i.Add(Instruction.Create(OpCodes.Call, compiledBuildMethod)); } @@ -482,6 +546,11 @@ namespace Avalonia.Build.Tasks i.Add(Instruction.Create(OpCodes.Ret)); i.Add(nop); } + else + { + engine.LogWarning(BuildEngineErrorCode.Loader, "", + $"XAML resource \"{res.Uri}\" won't be reachable via runtime loader, as no public constructor was found"); + } } } diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs index 728e81b198..eea3d3ad08 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Styling; @@ -31,9 +32,10 @@ namespace Avalonia.Themes.Fluent /// /// Initializes a new instance of the class. /// - public FluentTheme() + /// The parent's service provider. + public FluentTheme(IServiceProvider? sp = null) { - AvaloniaXamlLoader.Load(this); + AvaloniaXamlLoader.Load(sp, this); _baseDark = (IResourceDictionary)GetAndRemove("BaseDark"); _fluentDark = (IResourceDictionary)GetAndRemove("FluentDark"); diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs index af9d305043..56c4cbeac6 100644 --- a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs +++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Styling; @@ -16,9 +17,10 @@ namespace Avalonia.Themes.Simple /// /// Initializes a new instance of the class. /// - public SimpleTheme() + /// The parent's service provider. + public SimpleTheme(IServiceProvider? sp = null) { - AvaloniaXamlLoader.Load(this); + AvaloniaXamlLoader.Load(sp, this); _simpleDark = (IResourceDictionary)GetAndRemove("BaseDark"); _simpleLight = (IResourceDictionary)GetAndRemove("BaseLight"); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs index cc29d5ccb5..1bb05e238c 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs @@ -163,8 +163,9 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer public bool NeedsParentStack => true; public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) { + codeGen.Ldloc(context.ContextLocal); var method = context.GetAvaloniaTypes().RuntimeHelpers - .FindMethod(m => m.Name == "CreateRootServiceProviderV2"); + .FindMethod(m => m.Name == "CreateRootServiceProviderV3"); codeGen.EmitCall(method); return XamlILNodeEmitResult.Type(0, Type.GetClrType()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs index 35e2624ff9..0304165995 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs @@ -41,6 +41,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public bool NeedsParentStack => true; public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) { + if (_inheritContext) + { + codeGen.Ldloc(context.ContextLocal); + } + else + { + codeGen.Ldloc(context.ContextLocal); + var method = context.GetAvaloniaTypes().RuntimeHelpers + .FindMethod(m => m.Name == "CreateRootServiceProviderV3"); + codeGen.EmitCall(method); + } + codeGen.Ldloc(context.ContextLocal); return XamlILNodeEmitResult.Type(0, Type.GetClrType()); } diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs index 388ff61400..b5d222d979 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs @@ -23,6 +23,17 @@ namespace Avalonia.Markup.Xaml throw new XamlLoadException( $"No precompiled XAML found for {obj.GetType()}, make sure to specify x:Class and include your XAML file as AvaloniaResource"); } + + /// + /// Loads the XAML into a Avalonia component. + /// + /// The parent's service provider. + /// The object to load the XAML into. + public static void Load(IServiceProvider? sp, object obj) + { + throw new XamlLoadException( + $"No precompiled XAML found for {obj.GetType()}, make sure to specify x:Class and include your XAML file as AvaloniaResource"); + } /// /// Loads XAML from a URI. @@ -33,6 +44,20 @@ namespace Avalonia.Markup.Xaml /// /// The loaded object. public static object Load(Uri uri, Uri? baseUri = null) + { + return Load(null, uri, baseUri); + } + + /// + /// Loads XAML from a URI. + /// + /// The parent's service provider. + /// The URI of the XAML file. + /// + /// A base URI to use if is relative. + /// + /// The loaded object. + public static object Load(IServiceProvider? sp, Uri uri, Uri? baseUri = null) { if (uri is null) throw new ArgumentNullException(nameof(uri)); @@ -51,13 +76,25 @@ namespace Avalonia.Markup.Xaml var compiledLoader = assetLocator.GetAssembly(uri, baseUri) ?.GetType("CompiledAvaloniaXaml.!XamlLoader") - ?.GetMethod("TryLoad", new[] {typeof(string)}); + ?.GetMethod("TryLoad", new[] { typeof(System.IServiceProvider), typeof(string) }); if (compiledLoader != null) { - var compiledResult = compiledLoader.Invoke(null, new object[] {absoluteUri.ToString()}); + var compiledResult = compiledLoader.Invoke(null, new object?[] { sp, absoluteUri.ToString()}); if (compiledResult != null) return compiledResult; } + else + { + compiledLoader = assetLocator.GetAssembly(uri, baseUri) + ?.GetType("CompiledAvaloniaXaml.!XamlLoader") + ?.GetMethod("TryLoad", new[] {typeof(string)}); + if (compiledLoader != null) + { + var compiledResult = compiledLoader.Invoke(null, new object?[] {absoluteUri.ToString()}); + if (compiledResult != null) + return compiledResult; + } + } // This is intended for unit-tests only var runtimeLoader = AvaloniaLocator.Current.GetService(); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index 5bff9d9ca6..84b4f3bdba 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -34,13 +34,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions _ => null, }; - if (provideTarget.TargetObject is Setter setter) + if (provideTarget.TargetObject is Setter { Property: not null } setter) { targetType = setter.Property.PropertyType; } - - var previousWasControlTheme = false; - + // Look upwards though the ambient context for IResourceNodes // which might be able to give us the resource. foreach (var parent in stack.Parents) @@ -49,21 +47,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { return ColorToBrushConverter.Convert(value, targetType); } - - // HACK: Temporary fix for #8678. Hard-coded to only work for the DevTools main - // window as we don't want 3rd parties to start relying on this hack. - // - // We need to implement compile-time merging of resource dictionaries and this - // hack can be removed. - if (previousWasControlTheme && - parent is IResourceProvider hack && - hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" && - hack.Owner.TryGetResource(ResourceKey, out value)) - { - return ColorToBrushConverter.Convert(value, targetType); - } - - previousWasControlTheme = parent is ControlTheme; } if (provideTarget.TargetObject is Control target && diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs index d7eb328715..8bbf233ed9 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs @@ -10,6 +10,7 @@ namespace Avalonia.Markup.Xaml.Styling /// public class ResourceInclude : IResourceProvider { + private readonly IServiceProvider _serviceProvider; private readonly Uri? _baseUri; private IResourceDictionary? _loaded; private bool _isLoading; @@ -29,6 +30,7 @@ namespace Avalonia.Markup.Xaml.Styling /// The XAML service provider. public ResourceInclude(IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; _baseUri = serviceProvider.GetContextBaseUri(); } @@ -43,7 +45,7 @@ namespace Avalonia.Markup.Xaml.Styling { _isLoading = true; var source = Source ?? throw new InvalidOperationException("ResourceInclude.Source must be set."); - _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(source, _baseUri); + _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(_serviceProvider, source, _baseUri); _isLoading = false; } diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 2b75e0dd54..30e8ef4d02 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -12,6 +12,7 @@ namespace Avalonia.Markup.Xaml.Styling /// public class StyleInclude : IStyle, IResourceProvider { + private readonly IServiceProvider _serviceProvider; private readonly Uri? _baseUri; private IStyle[]? _loaded; private bool _isLoading; @@ -31,6 +32,7 @@ namespace Avalonia.Markup.Xaml.Styling /// The XAML service provider. public StyleInclude(IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; _baseUri = serviceProvider.GetContextBaseUri(); } @@ -52,7 +54,7 @@ namespace Avalonia.Markup.Xaml.Styling { _isLoading = true; var source = Source ?? throw new InvalidOperationException("StyleInclude.Source must be set."); - var loaded = (IStyle)AvaloniaXamlLoader.Load(source, _baseUri); + var loaded = (IStyle)AvaloniaXamlLoader.Load(_serviceProvider, source, _baseUri); _loaded = new[] { loaded }; _isLoading = false; } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index eec5d62fd3..2ca9a66fdc 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -167,18 +167,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime #line hidden public static IServiceProvider CreateRootServiceProviderV2() { - return new RootServiceProvider(new NameScope()); + return new RootServiceProvider(new NameScope(), null); + } + public static IServiceProvider CreateRootServiceProviderV3(IServiceProvider parentServiceProvider) + { + return new RootServiceProvider(new NameScope(), parentServiceProvider); } #line default - class RootServiceProvider : IServiceProvider, IAvaloniaXamlIlParentStackProvider + class RootServiceProvider : IServiceProvider { private readonly INameScope _nameScope; + private readonly IServiceProvider _parentServiceProvider; private readonly IRuntimePlatform _runtimePlatform; - public RootServiceProvider(INameScope nameScope) + public RootServiceProvider(INameScope nameScope, IServiceProvider parentServiceProvider) { _nameScope = nameScope; + _parentServiceProvider = parentServiceProvider; _runtimePlatform = AvaloniaLocator.Current.GetService(); } @@ -187,19 +193,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime if (serviceType == typeof(INameScope)) return _nameScope; if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) - return this; + return _parentServiceProvider?.GetService() + ?? DefaultAvaloniaXamlIlParentStackProvider.Instance; if (serviceType == typeof(IRuntimePlatform)) return _runtimePlatform ?? throw new KeyNotFoundException($"{nameof(IRuntimePlatform)} was not registered"); return null; } - public IEnumerable Parents + private class DefaultAvaloniaXamlIlParentStackProvider : IAvaloniaXamlIlParentStackProvider { - get + public static DefaultAvaloniaXamlIlParentStackProvider Instance { get; } = new(); + + public IEnumerable Parents { - if (Application.Current != null) - yield return Application.Current; + get + { + if (Application.Current != null) + yield return Application.Current; + } } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index bcb4bac457..a9528edc91 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -30,6 +30,7 @@ + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs index 8eed5013a2..f148d95bf9 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs @@ -2,6 +2,10 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Markup.Xaml.Styling; +using Avalonia.Markup.Xaml.XamlIl.Runtime; +using Avalonia.Media; +using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Themes.Simple; using Avalonia.UnitTests; @@ -265,4 +269,43 @@ public class StyleIncludeTests Assert.IsType(control.Styles[0]); Assert.IsType(control.Styles[1]); } + + [Fact] + public void StyleInclude_From_CodeBehind_Resolves_Compiled() + { + using var locatorScope = AvaloniaLocator.EnterScope(); + AvaloniaLocator.CurrentMutable.BindToSelf(new AssetLoader(GetType().Assembly)); + + var sp = new TestServiceProvider(); + var styleInclude = new StyleInclude(sp) + { + Source = new Uri("avares://Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml") + }; + + var loaded = Assert.IsType(styleInclude.Loaded); + + Assert.Equal( + sp.GetService().Parents, + loaded.ServiceProvider.GetService().Parents); + } + + private class TestServiceProvider : IServiceProvider, IUriContext, IAvaloniaXamlIlParentStackProvider + { + private IServiceProvider _root = XamlIlRuntimeHelpers.CreateRootServiceProviderV2(); + public object GetService(Type serviceType) + { + if (serviceType == typeof(IUriContext)) + { + return this; + } + if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) + { + return this; + } + return _root.GetService(serviceType); + } + + public Uri BaseUri { get; set; } + public IEnumerable Parents { get; } = new[] { new ContentControl() }; + } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml new file mode 100644 index 0000000000..987c0f321a --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml @@ -0,0 +1,5 @@ + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs new file mode 100644 index 0000000000..ceb122f05c --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml.cs @@ -0,0 +1,16 @@ +using System; +using Avalonia.Controls; +using Avalonia.Styling; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml; + +public class StyleWithServiceLocator : Style +{ + public IServiceProvider ServiceProvider { get; } + + public StyleWithServiceLocator(IServiceProvider sp = null) + { + ServiceProvider = sp; + AvaloniaXamlLoader.Load(sp, this); + } +} From afa7c320282734392f04901be46106e3177d99e4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 30 Nov 2022 23:27:37 -0500 Subject: [PATCH 050/173] Fix merge conflict --- ...niaXamlIlConstructorServiceProviderTransformer.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs index 0304165995..35e2624ff9 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlConstructorServiceProviderTransformer.cs @@ -41,18 +41,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public bool NeedsParentStack => true; public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) { - if (_inheritContext) - { - codeGen.Ldloc(context.ContextLocal); - } - else - { - codeGen.Ldloc(context.ContextLocal); - var method = context.GetAvaloniaTypes().RuntimeHelpers - .FindMethod(m => m.Name == "CreateRootServiceProviderV3"); - codeGen.EmitCall(method); - } - codeGen.Ldloc(context.ContextLocal); return XamlILNodeEmitResult.Type(0, Type.GetClrType()); } From 1caf79aa93158297629be9a82e3ef1140525ccd2 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 1 Dec 2022 01:26:51 -0500 Subject: [PATCH 051/173] Fix build --- .../Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs index c81a3c1416..ed7bb98833 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs @@ -30,7 +30,8 @@ public class MergeResourceInclude : IResourceProvider if (_loaded == null) { _isLoading = true; - _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(_serviceProvider, Source, _baseUri); + var source = Source ?? throw new InvalidOperationException("MergeResourceInclude.Source must be set."); + _loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(source, _baseUri); _isLoading = false; } From f4898667345648ac27b08a8e77626fcda6971d0f Mon Sep 17 00:00:00 2001 From: zhouzj Date: Thu, 1 Dec 2022 16:28:34 +0800 Subject: [PATCH 052/173] Replace ContentControl to ContentPresenter --- .../Controls/CalendarButton.xaml | 16 ++++++++-------- .../Controls/CalendarButton.xaml | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml index 06b6cf30c2..76b51ca819 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml @@ -35,13 +35,13 @@ - + @@ -70,7 +70,7 @@ - diff --git a/src/Avalonia.Themes.Simple/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Simple/Controls/CalendarButton.xaml index 59fb24663f..9e13e1e996 100644 --- a/src/Avalonia.Themes.Simple/Controls/CalendarButton.xaml +++ b/src/Avalonia.Themes.Simple/Controls/CalendarButton.xaml @@ -32,14 +32,14 @@ Opacity="0.5" /> - + - From 2f3c5ef98fefb19a81f59870cc1fbc7645477469 Mon Sep 17 00:00:00 2001 From: ShadowMarker789 <37165910+ShadowMarker789@users.noreply.github.com> Date: Thu, 1 Dec 2022 17:42:44 +0800 Subject: [PATCH 053/173] Add Support for low-latency Dxgi-swapchain based presentation model (#9572) * Got the swapchain working * Removed unneeded field * Code cleanup * Fixed and added custom control with smiles. * Update DXGI_SWAP_CHAIN_FLAG.cs All of the new members should be internal. Leaving the minimum (handles?) for the interoperability. * > Please, use our COM interop codegen instead. It provides at least some safety with reference tracking / disposal DONE * Code cleanup, using statements for correct disposal, disposing of fields in the render-target. * Further code refactoring, reorganized folder structure under DirectX Co-authored-by: michael.david.howard@outlook.com --- samples/ControlCatalog.NetCore/Program.cs | 9 + .../Properties/launchSettings.json | 11 + samples/ControlCatalog/ControlCatalog.csproj | 10 + .../Converter/DegToRadConverter.cs | 29 + samples/ControlCatalog/MainView.xaml | 3 + .../ControlCatalog/Pages/CustomDrawing.xaml | 107 ++ .../Pages/CustomDrawing.xaml.cs | 67 + .../Pages/CustomDrawingExampleControl.cs | 215 +++ .../Avalonia.Win32/Avalonia.Win32.csproj | 1 + .../Avalonia.Win32/DirectX/DirectXEnums.cs | 139 ++ .../Avalonia.Win32/DirectX/DirectXStructs.cs | 1370 +++++++++++++++++ .../DirectX/DirectXUnmanagedMethods.cs | 29 + .../Avalonia.Win32/DirectX/DxgiConnection.cs | 202 +++ .../DirectX/DxgiRenderTarget.cs | 184 +++ .../DirectX/DxgiSwapchainWindow.cs | 32 + .../Avalonia.Win32/DirectX/directx.idl | 305 ++++ src/Windows/Avalonia.Win32/Win32GlManager.cs | 5 + src/Windows/Avalonia.Win32/Win32Platform.cs | 10 + src/Windows/Avalonia.Win32/WindowImpl.cs | 17 + 19 files changed, 2745 insertions(+) create mode 100644 samples/ControlCatalog.NetCore/Properties/launchSettings.json create mode 100644 samples/ControlCatalog/Converter/DegToRadConverter.cs create mode 100644 samples/ControlCatalog/Pages/CustomDrawing.xaml create mode 100644 samples/ControlCatalog/Pages/CustomDrawing.xaml.cs create mode 100644 samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/DirectXEnums.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/DirectXStructs.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/DirectXUnmanagedMethods.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/DxgiConnection.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/DxgiRenderTarget.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/DxgiSwapchainWindow.cs create mode 100644 src/Windows/Avalonia.Win32/DirectX/directx.idl diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index b1bacc6483..d5e5cb14dc 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -99,6 +99,15 @@ namespace ControlCatalog.NetCore SilenceConsole(); return builder.StartLinuxDrm(args, scaling: GetScaling()); } + else if (args.Contains("--dxgi")) + { + builder.With(new Win32PlatformOptions() + { + UseLowLatencyDxgiSwapChain = true, + UseWindowsUIComposition = false + }); + return builder.StartWithClassicDesktopLifetime(args); + } else return builder.StartWithClassicDesktopLifetime(args); } diff --git a/samples/ControlCatalog.NetCore/Properties/launchSettings.json b/samples/ControlCatalog.NetCore/Properties/launchSettings.json new file mode 100644 index 0000000000..5964ca320e --- /dev/null +++ b/samples/ControlCatalog.NetCore/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "ControlCatalog.NetCore": { + "commandName": "Project" + }, + "Dxgi": { + "commandName": "Project", + "commandLineArgs": "--dxgi" + } + } +} \ No newline at end of file diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 6b550a30be..18f0dd16ba 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -33,4 +33,14 @@ + + + + + + + + + + diff --git a/samples/ControlCatalog/Converter/DegToRadConverter.cs b/samples/ControlCatalog/Converter/DegToRadConverter.cs new file mode 100644 index 0000000000..b062bcb64a --- /dev/null +++ b/samples/ControlCatalog/Converter/DegToRadConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter +{ + public class DegToRadConverter : IValueConverter + { + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double rad) + { + return rad * 180.0d / Math.PI; + } + return 0.0d; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double deg) + { + return deg / 180.0d * Math.PI; + } + return 0.0d; + } + } +} diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index b5a09b5fbd..b95b455ca4 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -66,6 +66,9 @@ + + + diff --git a/samples/ControlCatalog/Pages/CustomDrawing.xaml b/samples/ControlCatalog/Pages/CustomDrawing.xaml new file mode 100644 index 0000000000..04b7fcfea5 --- /dev/null +++ b/samples/ControlCatalog/Pages/CustomDrawing.xaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +