diff --git a/scripts/ReplaceNugetCache.sh b/scripts/ReplaceNugetCache.sh index 4cc11edd60..e1c0487d60 100755 --- a/scripts/ReplaceNugetCache.sh +++ b/scripts/ReplaceNugetCache.sh @@ -2,7 +2,6 @@ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp2.0/ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/ - cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard2.0/ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.skia/$1/lib/netstandard2.0/ cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.native/$1/lib/netstandard2.0/ diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index 1fcfb525cb..037e80e372 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -28,6 +28,11 @@ namespace Avalonia { Diagnostics.DevTools.Attach(control, gesture); } + + public static void OpenDevTools(this TopLevel control) + { + Diagnostics.DevTools.OpenDevTools(control); + } } } @@ -73,7 +78,7 @@ namespace Avalonia.Diagnostics RoutingStrategies.Tunnel); } - private static void OpenDevTools(TopLevel control) + internal static void OpenDevTools(TopLevel control) { if (s_open.TryGetValue(control, out var devToolsWindow)) { diff --git a/src/Avalonia.Native/MacOSMountedVolumeInfoProvider.cs b/src/Avalonia.Native/MacOSMountedVolumeInfoProvider.cs index eea695d77e..92b2915e2e 100644 --- a/src/Avalonia.Native/MacOSMountedVolumeInfoProvider.cs +++ b/src/Avalonia.Native/MacOSMountedVolumeInfoProvider.cs @@ -8,16 +8,16 @@ using Avalonia.Controls.Platform; namespace Avalonia.Native { - internal class WindowsMountedVolumeInfoListener : IDisposable + internal class MacOSMountedVolumeInfoListener : IDisposable { private readonly CompositeDisposable _disposables; - private readonly ObservableCollection _targetObs; private bool _beenDisposed = false; private ObservableCollection mountedDrives; - public WindowsMountedVolumeInfoListener(ObservableCollection mountedDrives) + public MacOSMountedVolumeInfoListener(ObservableCollection mountedDrives) { this.mountedDrives = mountedDrives; + _disposables = new CompositeDisposable(); var pollTimer = Observable.Interval(TimeSpan.FromSeconds(1)) @@ -30,7 +30,8 @@ namespace Avalonia.Native private void Poll(long _) { - var mountVolInfos = Directory.GetDirectories("/Volumes") + var mountVolInfos = Directory.GetDirectories("/Volumes/") + .Where(p=> p != null) .Select(p => new MountedVolumeInfo() { VolumeLabel = Path.GetFileName(p), @@ -38,15 +39,15 @@ namespace Avalonia.Native VolumeSizeBytes = 0 }) .ToArray(); - - if (_targetObs.SequenceEqual(mountVolInfos)) + + if (mountedDrives.SequenceEqual(mountVolInfos)) return; else { - _targetObs.Clear(); + mountedDrives.Clear(); foreach (var i in mountVolInfos) - _targetObs.Add(i); + mountedDrives.Add(i); } } @@ -72,7 +73,7 @@ namespace Avalonia.Native public IDisposable Listen(ObservableCollection mountedDrives) { Contract.Requires(mountedDrives != null); - return new WindowsMountedVolumeInfoListener(mountedDrives); + return new MacOSMountedVolumeInfoListener(mountedDrives); } } } diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Visuals/Vector.cs index 2f1690184d..11bda8b00e 100644 --- a/src/Avalonia.Visuals/Vector.cs +++ b/src/Avalonia.Visuals/Vector.cs @@ -65,9 +65,7 @@ namespace Avalonia /// Second vector /// The dot product public static double operator *(Vector a, Vector b) - { - return a.X * b.X + a.Y * b.Y; - } + => Dot(a, b); /// /// Scales a vector. @@ -76,9 +74,7 @@ namespace Avalonia /// The scaling factor. /// The scaled vector. public static Vector operator *(Vector vector, double scale) - { - return new Vector(vector._x * scale, vector._y * scale); - } + => Multiply(vector, scale); /// /// Scales a vector. @@ -87,14 +83,17 @@ namespace Avalonia /// The divisor. /// The scaled vector. public static Vector operator /(Vector vector, double scale) - { - return new Vector(vector._x / scale, vector._y / scale); - } + => Divide(vector, scale); /// /// Length of the vector /// - public double Length => Math.Sqrt(X * X + Y * Y); + public double Length => Math.Sqrt(SquaredLength); + + /// + /// Squared Length of the vector + /// + public double SquaredLength => _x * _x + _y * _y; /// /// Negates a vector. @@ -102,9 +101,7 @@ namespace Avalonia /// The vector. /// The negated vector. public static Vector operator -(Vector a) - { - return new Vector(-a._x, -a._y); - } + => Negate(a); /// /// Adds two vectors. @@ -113,9 +110,7 @@ namespace Avalonia /// The second vector. /// A vector that is the result of the addition. public static Vector operator +(Vector a, Vector b) - { - return new Vector(a._x + b._x, a._y + b._y); - } + => Add(a, b); /// /// Subtracts two vectors. @@ -124,9 +119,7 @@ namespace Avalonia /// The second vector. /// A vector that is the result of the subtraction. public static Vector operator -(Vector a, Vector b) - { - return new Vector(a._x - b._x, a._y - b._y); - } + => Subtract(a, b); /// /// Check if two vectors are equal (bitwise). @@ -155,7 +148,8 @@ namespace Avalonia public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + return false; return obj is Vector vector && Equals(vector); } @@ -206,5 +200,131 @@ namespace Avalonia { return new Vector(_x, y); } + + /// + /// Returns a normalized version of this vector. + /// + /// The normalized vector. + public Vector Normalize() + => Normalize(this); + + /// + /// Returns a negated version of this vector. + /// + /// The negated vector. + public Vector Negate() + => Negate(this); + + /// + /// Returns the dot product of two vectors. + /// + /// The first vector. + /// The second vector. + /// The dot product. + public static double Dot(Vector a, Vector b) + => a._x * b._x + a._y * b._y; + + /// + /// Returns the cross product of two vectors. + /// + /// The first vector. + /// The second vector. + /// The cross product. + public static double Cross(Vector a, Vector b) + => a._x * b._y - a._y * b._x; + + /// + /// Normalizes the given vector. + /// + /// The vector + /// The normalized vector. + public static Vector Normalize(Vector vector) + => Divide(vector, vector.Length); + + /// + /// Divides the first vector by the second. + /// + /// The first vector. + /// The second vector. + /// The scaled vector. + public static Vector Divide(Vector a, Vector b) + => new Vector(a._x / b._x, a._y / b._y); + + /// + /// Divides the vector by the given scalar. + /// + /// The vector + /// The scalar value + /// The scaled vector. + public static Vector Divide(Vector vector, double scalar) + => new Vector(vector._x / scalar, vector._y / scalar); + + /// + /// Multiplies the first vector by the second. + /// + /// The first vector. + /// The second vector. + /// The scaled vector. + public static Vector Multiply(Vector a, Vector b) + => new Vector(a._x * b._x, a._y * b._y); + + /// + /// Multiplies the vector by the given scalar. + /// + /// The vector + /// The scalar value + /// The scaled vector. + public static Vector Multiply(Vector vector, double scalar) + => new Vector(vector._x * scalar, vector._y * scalar); + + /// + /// Adds the second to the first vector + /// + /// The first vector. + /// The second vector. + /// The summed vector. + public static Vector Add(Vector a, Vector b) + => new Vector(a._x + b._x, a._y + b._y); + + /// + /// Subtracts the second from the first vector + /// + /// The first vector. + /// The second vector. + /// The difference vector. + public static Vector Subtract(Vector a, Vector b) + => new Vector(a._x - b._x, a._y - b._y); + + /// + /// Negates the vector + /// + /// The vector to negate. + /// The scaled vector. + public static Vector Negate(Vector vector) + => new Vector(-vector._x, -vector._y); + + /// + /// Returnes the vector (0.0, 0.0) + /// + public static Vector Zero + => new Vector(0, 0); + + /// + /// Returnes the vector (1.0, 1.0) + /// + public static Vector One + => new Vector(1, 1); + + /// + /// Returnes the vector (1.0, 0.0) + /// + public static Vector UnitX + => new Vector(1, 0); + + /// + /// Returnes the vector (0.0, 1.0) + /// + public static Vector UnitY + => new Vector(0, 1); } } diff --git a/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoListener.cs b/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoListener.cs index 102e027584..a17e6b8b51 100644 --- a/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoListener.cs +++ b/src/Windows/Avalonia.Win32/WindowsMountedVolumeInfoListener.cs @@ -10,8 +10,7 @@ namespace Avalonia.Win32 { internal class WindowsMountedVolumeInfoListener : IDisposable { - private readonly CompositeDisposable _disposables; - private readonly ObservableCollection _targetObs = new ObservableCollection(); + private readonly CompositeDisposable _disposables; private bool _beenDisposed = false; private ObservableCollection mountedDrives; @@ -41,14 +40,14 @@ namespace Avalonia.Win32 }) .ToArray(); - if (_targetObs.SequenceEqual(mountVolInfos)) + if (mountedDrives.SequenceEqual(mountVolInfos)) return; else { - _targetObs.Clear(); + mountedDrives.Clear(); foreach (var i in mountVolInfos) - _targetObs.Add(i); + mountedDrives.Add(i); } } diff --git a/tests/Avalonia.Visuals.UnitTests/VectorTests.cs b/tests/Avalonia.Visuals.UnitTests/VectorTests.cs new file mode 100644 index 0000000000..1bcc165aef --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/VectorTests.cs @@ -0,0 +1,112 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Xunit; +using Avalonia; +using System; + +namespace Avalonia.Visuals.UnitTests +{ + public class VectorTests + { + [Fact] + public void Length_Should_Return_Correct_Length_Of_Vector() + { + var vector = new Vector(2, 4); + var length = Math.Sqrt(2 * 2 + 4 * 4); + + Assert.Equal(length, vector.Length); + } + + [Fact] + public void Length_Squared_Should_Return_Correct_Length_Of_Vector() + { + var vectorA = new Vector(2, 4); + var squaredLengthA = 2 * 2 + 4 * 4; + + Assert.Equal(squaredLengthA, vectorA.SquaredLength); + } + + [Fact] + public void Normalize_Should_Return_Normalized_Vector() + { + // the length of a normalized vector must be 1 + + var vectorA = new Vector(13, 84); + var vectorB = new Vector(-34, 345); + var vectorC = new Vector(-34, -84); + + Assert.Equal(1.0, vectorA.Normalize().Length); + Assert.Equal(1.0, vectorB.Normalize().Length); + Assert.Equal(1.0, vectorC.Normalize().Length); + } + + [Fact] + public void Negate_Should_Return_Negated_Vector() + { + var vector = new Vector(2, 4); + var negated = new Vector(-2, -4); + + Assert.Equal(negated, vector.Negate()); + } + + [Fact] + public void Dot_Should_Return_Correct_Value() + { + var a = new Vector(-6, 8.0); + var b = new Vector(5, 12.0); + + Assert.Equal(66.0, Vector.Dot(a, b)); + } + + [Fact] + public void Cross_Should_Return_Correct_Value() + { + var a = new Vector(-6, 8.0); + var b = new Vector(5, 12.0); + + Assert.Equal(-112.0, Vector.Cross(a, b)); + } + + [Fact] + public void Divied_By_Vector_Should_Return_Correct_Value() + { + var a = new Vector(10, 2); + var b = new Vector(5, 2); + + var expected = new Vector(2, 1); + + Assert.Equal(expected, Vector.Divide(a, b)); + } + + [Fact] + public void Divied_Should_Return_Correct_Value() + { + var vector = new Vector(10, 2); + var expected = new Vector(5, 1); + + Assert.Equal(expected, Vector.Divide(vector, 2)); + } + + [Fact] + public void Multiply_By_Vector_Should_Return_Correct_Value() + { + var a = new Vector(10, 2); + var b = new Vector(2, 2); + + var expected = new Vector(20, 4); + + Assert.Equal(expected, Vector.Multiply(a, b)); + } + + [Fact] + public void Multiply_Should_Return_Correct_Value() + { + var vector = new Vector(10, 2); + + var expected = new Vector(20, 4); + + Assert.Equal(expected, Vector.Multiply(vector, 2)); + } + } +}