From 92880f6d037de3a5e62f770a32bda4a33edc92bc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 Jun 2020 17:32:02 -0300 Subject: [PATCH 001/446] force dx11 --- samples/ControlCatalog.NetCore/Program.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 5df8c1be64..06cf3e6b61 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using Avalonia; using Avalonia.Dialogs; +using Avalonia.OpenGL; using Avalonia.ReactiveUI; namespace ControlCatalog.NetCore @@ -65,6 +66,10 @@ namespace ControlCatalog.NetCore EnableMultitouch = true, AllowEglInitialization = true }) + .With(new AngleOptions + { + AllowedPlatformApis = new System.Collections.Generic.List { AngleOptions.PlatformApi.DirectX11} + }) .UseSkia() .UseReactiveUI() .UseManagedSystemDialogs() From 459e9db2fb04a05abbf830363db5fed73eed64c3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 Jun 2020 17:35:43 -0300 Subject: [PATCH 002/446] add Composition interfaces. --- .../Avalonia.Win32/Avalonia.Win32.csproj | 1 + .../Composition/CompositionHost.cs | 145 ++++++++++++ .../Avalonia.Win32/Composition/D2DEffects.cs | 91 ++++++++ .../GRAPHICS_EFFECT_PROPERTY_MAPPING.cs | 18 ++ .../Composition/GaussianBlurEffect.cs | 79 +++++++ .../Composition/ICompositorDesktopInterop.cs | 14 ++ .../Composition/ICompositorDesktopInterop1.cs | 69 ++++++ .../Composition/ICompositorInterop.cs | 139 +++++++++++ .../Composition/IGraphicsEffectD2D1Interop.cs | 24 ++ .../IGraphicsEffectD2D1Interop1.cs | 217 ++++++++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 2 + 11 files changed, 799 insertions(+) create mode 100644 src/Windows/Avalonia.Win32/Composition/CompositionHost.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/D2DEffects.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index 49700710e9..34f71daf52 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs new file mode 100644 index 0000000000..223c208829 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -0,0 +1,145 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia.OpenGL.Angle; +using Windows.UI.Composition; +using WinRT; + +namespace Avalonia.Win32 +{ + class CompositionHost + { + internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE + { + DQTAT_COM_NONE = 0, + DQTAT_COM_ASTA = 1, + DQTAT_COM_STA = 2 + }; + + internal enum DISPATCHERQUEUE_THREAD_TYPE + { + DQTYPE_THREAD_DEDICATED = 1, + DQTYPE_THREAD_CURRENT = 2, + }; + + [StructLayout(LayoutKind.Sequential)] + internal struct DispatcherQueueOptions + { + public int dwSize; + + [MarshalAs(UnmanagedType.I4)] + public DISPATCHERQUEUE_THREAD_TYPE threadType; + + [MarshalAs(UnmanagedType.I4)] + public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; + }; + + [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)] + internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController); + + public static CompositionHost Instance { get; } = new CompositionHost(); + + private Compositor _compositor; + private Windows.System.DispatcherQueueController _dispatcherQueueController; + private Windows.UI.Composition.Desktop.DesktopWindowTarget _target; + + private CompositionHost() + { + } + + public void AddElement(float size, float x, float y) + { + if (_target.Root != null) + { + var visuals = _target.Root.As().Children; + + var visual = _compositor.CreateSpriteVisual(); + + var element = _compositor.CreateSpriteVisual(); + var rand = new Random(); + + element.Brush = _compositor.CreateColorBrush(new Windows.UI.Color { A = 255, R = (byte)(rand.NextDouble() * 255), G = (byte)(rand.NextDouble() * 255), B = (byte)(rand.NextDouble() * 255) }); + element.Size = new System.Numerics.Vector2(size, size); + element.Offset = new System.Numerics.Vector3(x, y, 0.0f); + + var animation = _compositor.CreateVector3KeyFrameAnimation(); + var bottom = (float)600 - element.Size.Y; + animation.InsertKeyFrame(1, new System.Numerics.Vector3(element.Offset.X, bottom, 0)); + + animation.Duration = TimeSpan.FromSeconds(2); + animation.DelayTime = TimeSpan.FromSeconds(3); + element.StartAnimation("Offset", animation); + visuals.InsertAtTop(element); + + visuals.InsertAtTop(visual); + } + } + + public void Initialize(IntPtr hwnd) + { + EnsureDispatcherQueue(); + if (_dispatcherQueueController != null) + _compositor = new Windows.UI.Composition.Compositor(); + + CreateDesktopWindowTarget(hwnd); + CreateCompositionRoot(); + + var interop = _compositor.As(); + + var display = Win32GlManager.EglFeature.Display as AngleWin32EglDisplay; + + var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); + + gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100,100), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); + + } + + public void CreateBlur() + { + var effect = new GaussianBlurEffect(); + var effectFactory = _compositor.CreateEffectFactory(effect); + var blurBrush = effectFactory.CreateBrush(); + + var backDropBrush = _compositor.CreateBackdropBrush(); + + blurBrush.SetSourceParameter("backdrop", backDropBrush); + + var visual = _compositor.CreateSpriteVisual(); + + visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); + visual.Brush = blurBrush; + + _target.Root = visual; + } + + void CreateCompositionRoot() + { + var root = _compositor.CreateContainerVisual(); + root.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); + //root.Offset = new System.Numerics.Vector3(0, 0, 0); + _target.Root = root; + } + + void CreateDesktopWindowTarget(IntPtr window) + { + var interop = _compositor.As(); + + interop.CreateDesktopWindowTarget(window, false, out var windowTarget); + _target = Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); + } + + void EnsureDispatcherQueue() + { + if (_dispatcherQueueController == null) + { + DispatcherQueueOptions options = new DispatcherQueueOptions(); + options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA; + options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT; + options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions)); + + CreateDispatcherQueueController(options, out var queue); + _dispatcherQueueController = Windows.System.DispatcherQueueController.FromAbi(queue); + } + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs new file mode 100644 index 0000000000..1c761ee082 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs @@ -0,0 +1,91 @@ +using System; + + +namespace Avalonia.Win32 +{ + class D2DEffects + { + public static readonly Guid CLSID_D2D12DAffineTransform = new Guid(0x6AA97485, 0x6354, 0x4CFC, 0x90, 0x8C, 0xE4, 0xA7, 0x4F, 0x62, 0xC9, 0x6C); + + public static readonly Guid CLSID_D2D13DPerspectiveTransform = new Guid(0xC2844D0B, 0x3D86, 0x46E7, 0x85, 0xBA, 0x52, 0x6C, 0x92, 0x40, 0xF3, 0xFB); + + public static readonly Guid CLSID_D2D13DTransform = new Guid(0xE8467B04, 0xEC61, 0x4B8A, 0xB5, 0xDE, 0xD4, 0xD7, 0x3D, 0xEB, 0xEA, 0x5A); + + public static readonly Guid CLSID_D2D1ArithmeticComposite = new Guid(0xFC151437, 0x049A, 0x4784, 0xA2, 0x4A, 0xF1, 0xC4, 0xDA, 0xF2, 0x09, 0x87); + + public static readonly Guid CLSID_D2D1Atlas = new Guid(0x913E2BE4, 0xFDCF, 0x4FE2, 0xA5, 0xF0, 0x24, 0x54, 0xF1, 0x4F, 0xF4, 0x08); + + public static readonly Guid CLSID_D2D1BitmapSource = new Guid(0x5FB6C24D, 0xC6DD, 0x4231, 0x94, 0x4, 0x50, 0xF4, 0xD5, 0xC3, 0x25, 0x2D); + + public static readonly Guid CLSID_D2D1Blend = new Guid(0x81C5B77B, 0x13F8, 0x4CDD, 0xAD, 0x20, 0xC8, 0x90, 0x54, 0x7A, 0xC6, 0x5D); + + public static readonly Guid CLSID_D2D1Border = new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27); + + public static readonly Guid CLSID_D2D1Brightness = new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87); + + public static readonly Guid CLSID_D2D1ColorManagement = new Guid(0x1A28524C, 0xFDD6, 0x4AA4, 0xAE, 0x8F, 0x83, 0x7E, 0xB8, 0x26, 0x7B, 0x37); + + public static readonly Guid CLSID_D2D1ColorMatrix = new Guid(0x921F03D6, 0x641C, 0x47DF, 0x85, 0x2D, 0xB4, 0xBB, 0x61, 0x53, 0xAE, 0x11); + + public static readonly Guid CLSID_D2D1Composite = new Guid(0x48FC9F51, 0xF6AC, 0x48F1, 0x8B, 0x58, 0x3B, 0x28, 0xAC, 0x46, 0xF7, 0x6D); + + public static readonly Guid CLSID_D2D1ConvolveMatrix = new Guid(0x407F8C08, 0x5533, 0x4331, 0xA3, 0x41, 0x23, 0xCC, 0x38, 0x77, 0x84, 0x3E); + + public static readonly Guid CLSID_D2D1Crop = new Guid(0xE23F7110, 0x0E9A, 0x4324, 0xAF, 0x47, 0x6A, 0x2C, 0x0C, 0x46, 0xF3, 0x5B); + + public static readonly Guid CLSID_D2D1DirectionalBlur = new Guid(0x174319A6, 0x58E9, 0x49B2, 0xBB, 0x63, 0xCA, 0xF2, 0xC8, 0x11, 0xA3, 0xDB); + + public static readonly Guid CLSID_D2D1DiscreteTransfer = new Guid(0x90866FCD, 0x488E, 0x454B, 0xAF, 0x06, 0xE5, 0x04, 0x1B, 0x66, 0xC3, 0x6C); + + public static readonly Guid CLSID_D2D1DisplacementMap = new Guid(0xEDC48364, 0x417, 0x4111, 0x94, 0x50, 0x43, 0x84, 0x5F, 0xA9, 0xF8, 0x90); + + public static readonly Guid CLSID_D2D1DistantDiffuse = new Guid(0x3E7EFD62, 0xA32D, 0x46D4, 0xA8, 0x3C, 0x52, 0x78, 0x88, 0x9A, 0xC9, 0x54); + + public static readonly Guid CLSID_D2D1DistantSpecular = new Guid(0x428C1EE5, 0x77B8, 0x4450, 0x8A, 0xB5, 0x72, 0x21, 0x9C, 0x21, 0xAB, 0xDA); + + public static readonly Guid CLSID_D2D1DpiCompensation = new Guid(0x6C26C5C7, 0x34E0, 0x46FC, 0x9C, 0xFD, 0xE5, 0x82, 0x37, 0x6, 0xE2, 0x28); + + public static readonly Guid CLSID_D2D1Flood = new Guid(0x61C23C20, 0xAE69, 0x4D8E, 0x94, 0xCF, 0x50, 0x07, 0x8D, 0xF6, 0x38, 0xF2); + + public static readonly Guid CLSID_D2D1GammaTransfer = new Guid(0x409444C4, 0xC419, 0x41A0, 0xB0, 0xC1, 0x8C, 0xD0, 0xC0, 0xA1, 0x8E, 0x42); + + public static readonly Guid CLSID_D2D1GaussianBlur = new Guid(0x1FEB6D69, 0x2FE6, 0x4AC9, 0x8C, 0x58, 0x1D, 0x7F, 0x93, 0xE7, 0xA6, 0xA5); + + public static readonly Guid CLSID_D2D1Scale = new Guid(0x9DAF9369, 0x3846, 0x4D0E, 0xA4, 0x4E, 0xC, 0x60, 0x79, 0x34, 0xA5, 0xD7); + + public static readonly Guid CLSID_D2D1Histogram = new Guid(0x881DB7D0, 0xF7EE, 0x4D4D, 0xA6, 0xD2, 0x46, 0x97, 0xAC, 0xC6, 0x6E, 0xE8); + + public static readonly Guid CLSID_D2D1HueRotation = new Guid(0x0F4458EC, 0x4B32, 0x491B, 0x9E, 0x85, 0xBD, 0x73, 0xF4, 0x4D, 0x3E, 0xB6); + + public static readonly Guid CLSID_D2D1LinearTransfer = new Guid(0xAD47C8FD, 0x63EF, 0x4ACC, 0x9B, 0x51, 0x67, 0x97, 0x9C, 0x03, 0x6C, 0x06); + + public static readonly Guid CLSID_D2D1LuminanceToAlpha = new Guid(0x41251AB7, 0x0BEB, 0x46F8, 0x9D, 0xA7, 0x59, 0xE9, 0x3F, 0xCC, 0xE5, 0xDE); + + public static readonly Guid CLSID_D2D1Morphology = new Guid(0xEAE6C40D, 0x626A, 0x4C2D, 0xBF, 0xCB, 0x39, 0x10, 0x01, 0xAB, 0xE2, 0x02); + + public static readonly Guid CLSID_D2D1OpacityMetadata = new Guid(0x6C53006A, 0x4450, 0x4199, 0xAA, 0x5B, 0xAD, 0x16, 0x56, 0xFE, 0xCE, 0x5E); + + public static readonly Guid CLSID_D2D1PointDiffuse = new Guid(0xB9E303C3, 0xC08C, 0x4F91, 0x8B, 0x7B, 0x38, 0x65, 0x6B, 0xC4, 0x8C, 0x20); + + public static readonly Guid CLSID_D2D1PointSpecular = new Guid(0x09C3CA26, 0x3AE2, 0x4F09, 0x9E, 0xBC, 0xED, 0x38, 0x65, 0xD5, 0x3F, 0x22); + + public static readonly Guid CLSID_D2D1Premultiply = new Guid(0x06EAB419, 0xDEED, 0x4018, 0x80, 0xD2, 0x3E, 0x1D, 0x47, 0x1A, 0xDE, 0xB2); + + public static readonly Guid CLSID_D2D1Saturation = new Guid(0x5CB2D9CF, 0x327D, 0x459F, 0xA0, 0xCE, 0x40, 0xC0, 0xB2, 0x08, 0x6B, 0xF7); + + public static readonly Guid CLSID_D2D1Shadow = new Guid(0xC67EA361, 0x1863, 0x4E69, 0x89, 0xDB, 0x69, 0x5D, 0x3E, 0x9A, 0x5B, 0x6B); + + public static readonly Guid CLSID_D2D1SpotDiffuse = new Guid(0x818A1105, 0x7932, 0x44F4, 0xAA, 0x86, 0x08, 0xAE, 0x7B, 0x2F, 0x2C, 0x93); + + public static readonly Guid CLSID_D2D1SpotSpecular = new Guid(0xEDAE421E, 0x7654, 0x4A37, 0x9D, 0xB8, 0x71, 0xAC, 0xC1, 0xBE, 0xB3, 0xC1); + + public static readonly Guid CLSID_D2D1TableTransfer = new Guid(0x5BF818C3, 0x5E43, 0x48CB, 0xB6, 0x31, 0x86, 0x83, 0x96, 0xD6, 0xA1, 0xD4); + + public static readonly Guid CLSID_D2D1Tile = new Guid(0xB0784138, 0x3B76, 0x4BC5, 0xB1, 0x3B, 0x0F, 0xA2, 0xAD, 0x02, 0x65, 0x9F); + + public static readonly Guid CLSID_D2D1Turbulence = new Guid(0xCF2BB6AE, 0x889A, 0x4AD7, 0xBA, 0x29, 0xA2, 0xFD, 0x73, 0x2C, 0x9F, 0xC9); + + public static readonly Guid CLSID_D2D1UnPremultiply = new Guid(0xFB9AC489, 0xAD8D, 0x41ED, 0x99, 0x99, 0xBB, 0x63, 0x47, 0xD1, 0x10, 0xF7); + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs new file mode 100644 index 0000000000..f5d5fc4ad3 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs @@ -0,0 +1,18 @@ +namespace Windows.Graphics.Effects.Interop +{ + public enum GRAPHICS_EFFECT_PROPERTY_MAPPING + { + GRAPHICS_EFFECT_PROPERTY_MAPPING_UNKNOWN, + GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORX, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORY, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORZ, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORW, + GRAPHICS_EFFECT_PROPERTY_MAPPING_RECT_TO_VECTOR4, + GRAPHICS_EFFECT_PROPERTY_MAPPING_RADIANS_TO_DEGREES, + GRAPHICS_EFFECT_PROPERTY_MAPPING_COLORMATRIX_ALPHA_MODE, + GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR3, + GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR4 + }; +} + diff --git a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs new file mode 100644 index 0000000000..19595e8977 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs @@ -0,0 +1,79 @@ +using System; +using Windows.Graphics.Effects; +using Windows.Graphics.Effects.Interop; +using Windows.UI.Composition; + + +namespace Avalonia.Win32 +{ + class GaussianBlurEffect : IGraphicsEffect, IGraphicsEffectSource, global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop + { + enum D2D1_GAUSSIANBLUR_OPTIMIZATION + { + D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED, + D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED, + D2D1_GAUSSIANBLUR_OPTIMIZATION_QUALITY, + D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD + }; + + enum D2D1_BORDER_MODE + { + D2D1_BORDER_MODE_SOFT, + D2D1_BORDER_MODE_HARD, + D2D1_BORDER_MODE_FORCE_DWORD + }; + + enum D2D1GaussianBlurProp + { + D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, + D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION, + D2D1_GAUSSIANBLUR_PROP_BORDER_MODE, + D2D1_GAUSSIANBLUR_PROP_FORCE_DWORD + }; + + public string Name { get; set; } + + public Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur; + + public uint PropertyCount => 3; + + public uint SourceCount => 1; + + public uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping) + { + throw new NotImplementedException(); + } + + public object GetProperty(uint index) + { + switch ((D2D1GaussianBlurProp)index) + { + case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION: + return 30.0f; + + case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION: + return (UInt32)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED; + + case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE: + return (UInt32)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD; + } + + return null; + } + + private IGraphicsEffectSource _source = new CompositionEffectSourceParameter("backdrop"); + + public IGraphicsEffectSource GetSource(uint index) + { + if (index == 0) + { + return _source; + } + else + { + return null; + } + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs new file mode 100644 index 0000000000..1d4cd3450f --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.InteropServices; +using WinRT; + +namespace Windows.UI.Composition.Desktop +{ + [WindowsRuntimeType] + [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] + public interface ICompositorDesktopInterop + { + void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test); + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs new file mode 100644 index 0000000000..1c3f06d679 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs @@ -0,0 +1,69 @@ +using WinRT; + +namespace ABI.Windows.UI.Composition.Desktop +{ + using global::System; + using global::System.Runtime.InteropServices; + + [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] + internal class ICompositorDesktopInterop : global::Windows.UI.Composition.Desktop.ICompositorDesktopInterop + + { + [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] + public struct Vftbl + { + public delegate int _CreateDesktopWindowTarget(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget); + + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public _CreateDesktopWindowTarget CreateDesktopWindowTarget; + + + public static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + CreateDesktopWindowTarget = Do_Abi_Create_Desktop_Window_Target + }; + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); + } + + private static int Do_Abi_Create_Desktop_Window_Target(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget) + { + try + { + ComWrappersSupport.FindObject(thisPtr).CreateDesktopWindowTarget(hwndTarget, isTopMost != 0, out desktopWindowTarget); + return 0; + } + catch (Exception ex) + { + desktopWindowTarget = IntPtr.Zero; + return Marshal.GetHRForException(ex); + } + } + } + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator ICompositorDesktopInterop(IObjectReference obj) => (obj != null) ? new ICompositorDesktopInterop(obj) : null; + protected readonly ObjectReference _obj; + public IObjectReference ObjRef { get => _obj; } + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + public ICompositorDesktopInterop(IObjectReference obj) : this(obj.As()) { } + internal ICompositorDesktopInterop(ObjectReference obj) + { + _obj = obj; + } + + public void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test) + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateDesktopWindowTarget(ThisPtr, hwndTarget, isTopmost ? (byte)1 : (byte)0, out test)); + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs new file mode 100644 index 0000000000..d9b25e497e --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs @@ -0,0 +1,139 @@ +using System; +using System.Runtime.InteropServices; +using WinRT; + +namespace Windows.UI.Composition.Interop +{ + [WindowsRuntimeType] + [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] + public interface ICompositorInterop + { + ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain); + + ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain); + + CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice); + } +} + +namespace ABI.Windows.UI.Composition.Interop +{ + using global::System; + using global::System.Runtime.InteropServices; + using global::Windows.UI.Composition; + + [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] + internal class ICompositorInterop : global::Windows.UI.Composition.Interop.ICompositorInterop + + { + [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] + public struct Vftbl + { + public delegate int _CreateCompositionSurfaceForHandle(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result); + public delegate int _CreateCompositionSurfaceForSwapChain(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result); + public delegate int _CreateGraphicsDevice(IntPtr ThisPtr, IntPtr renderingDevice, out IntPtr result); + + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public _CreateCompositionSurfaceForHandle CreateCompositionSurfaceForHandle; + public _CreateCompositionSurfaceForSwapChain CreateCompositionSurfaceForSwapChain; + public _CreateGraphicsDevice CreateGraphicsDevice; + + + public static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + + CreateCompositionSurfaceForHandle = Do_Abi_Create_Composition_Surface_For_Handle, + CreateCompositionSurfaceForSwapChain = Do_Abi_Create_Composition_Surface_For_SwapChain, + CreateGraphicsDevice= Do_Abi_Create_Graphics_Device + }; + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); + } + + private static int Do_Abi_Create_Composition_Surface_For_Handle(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface) + { + try + { + surface = IntPtr.Zero; + //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForHandle(swapChain); + return 0; + } + catch (Exception ex) + { + surface = IntPtr.Zero; + return Marshal.GetHRForException(ex); + } + } + + private static int Do_Abi_Create_Composition_Surface_For_SwapChain(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface) + { + try + { + surface = IntPtr.Zero; + //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForSwapChain(swapChain); + return 0; + } + catch (Exception ex) + { + surface = IntPtr.Zero; + return Marshal.GetHRForException(ex); + } + } + + private static int Do_Abi_Create_Graphics_Device(IntPtr thisPtr, IntPtr renderingDevice, out IntPtr graphicsDevice) + { + try + { + graphicsDevice = ComWrappersSupport.FindObject(thisPtr).CreateGraphicsDevice(renderingDevice).ThisPtr; + return 0; + } + catch (Exception ex) + { + graphicsDevice = IntPtr.Zero; + return Marshal.GetHRForException(ex); + } + } + } + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator ICompositorInterop(IObjectReference obj) => (obj != null) ? new ICompositorInterop(obj) : null; + protected readonly ObjectReference _obj; + public IObjectReference ObjRef { get => _obj; } + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + public ICompositorInterop(IObjectReference obj) : this(obj.As()) { } + internal ICompositorInterop(ObjectReference obj) + { + _obj = obj; + } + + public ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain) + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForHandle(ThisPtr, swapChain, out var compositionSurface)); + + return null; + } + + public ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain) + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForSwapChain(ThisPtr, swapChain, out var compositionSurface)); + + return null; + } + + public CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice) + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateGraphicsDevice(ThisPtr, renderingDevice, out var graphicsDevice)); + + return CompositionGraphicsDevice.FromAbi(graphicsDevice); + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs new file mode 100644 index 0000000000..74d3939a98 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.InteropServices; +using WinRT; + +namespace Windows.Graphics.Effects.Interop +{ + [WindowsRuntimeType] + [Guid("2FC57384-A068-44D7-A331-30982FCF7177")] + public interface IGraphicsEffectD2D1Interop + { + Guid EffectId { get; } + + uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping); + + object GetProperty(uint index); + + uint PropertyCount { get; } + + IGraphicsEffectSource GetSource(uint index); + + uint SourceCount { get; } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs new file mode 100644 index 0000000000..9d053c9e22 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs @@ -0,0 +1,217 @@ +using WinRT; + +namespace ABI.Windows.Graphics.Effects.Interop +{ + using global::System; + using global::System.Runtime.InteropServices; + + [Guid("2FC57384-A068-44D7-A331-30982FCF7177")] + internal class IGraphicsEffectD2D1Interop : global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop + + { + [Guid("2FC57384-A068-44D7-A331-30982FCF7177")] + public struct Vftbl + { + public delegate int _GetEffectId(IntPtr thisPtr, out Guid guid); + public delegate int _GetNamedPropertyMapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping); + public delegate int _GetProperty(IntPtr thisPtr, uint index, out IntPtr value); + public unsafe delegate int _GetPropertyCount(IntPtr thisPtr, uint* count); + public delegate int _GetSource(IntPtr thisPtr, uint index, out IntPtr source); + public delegate int _GetSourceCount(IntPtr thisPtr, out uint count); + + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public _GetEffectId GetEffectId; + public _GetNamedPropertyMapping GetNamedPropertyMapping; + public _GetPropertyCount GetPropertyCount; + public _GetProperty GetProperty; + public _GetSource GetSource; + public _GetSourceCount GetSourceCount; + + public static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + unsafe static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + GetEffectId = Do_Abi_Get_Effect_Id, + GetNamedPropertyMapping = Do_Abi_Get_Property_Mapping, + GetPropertyCount = Do_Abi_Get_Property_Count, + GetProperty = Do_Abi_Get_Property, + GetSource = Do_Abi_Get_Source, + GetSourceCount = Do_Abi_Get_Source_Count + + }; + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); + } + + private static int Do_Abi_Get_Effect_Id(IntPtr thisPtr, out Guid guid) + { + guid = default; + + try + { + guid = ComWrappersSupport.FindObject(thisPtr).EffectId; + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + + return 0; + } + + private static int Do_Abi_Get_Property_Mapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping) + { + try + { + ComWrappersSupport.FindObject(thisPtr).GetNamedPropertyMapping(MarshalString.FromAbi(name), out var mappingResult); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + + return 0; + } + + private static int Do_Abi_Get_Property(IntPtr thisPtr, uint index, out IntPtr value) + { + value = default; + + try + { + value = MarshalInspectable.CreateMarshaler( + ComWrappersSupport.FindObject(thisPtr).GetProperty(index)) + .As(Guid.Parse("4BD682DD-7554-40E9-9A9B-82654EDE7E62")) + .GetRef(); + + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + + return 0; + } + + unsafe private static int Do_Abi_Get_Property_Count(IntPtr thisPtr, uint* count) + { + + try + { + var res = ComWrappersSupport.FindObject(thisPtr).PropertyCount; + + if (count != null) + { + *count = res; + } + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + + return 0; + } + + private static int Do_Abi_Get_Source(IntPtr thisPtr, uint index, out IntPtr value) + { + value = default; + + try + { + var source = ComWrappersSupport.FindObject(thisPtr).GetSource(index); + + value = MarshalInterface.FromManaged(source); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + + return 0; + } + + private static int Do_Abi_Get_Source_Count(IntPtr thisPtr, out uint count) + { + count = default; + + try + { + count = ComWrappersSupport.FindObject(thisPtr).SourceCount; + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + + return 0; + } + } + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator IGraphicsEffectD2D1Interop(IObjectReference obj) => (obj != null) ? new IGraphicsEffectD2D1Interop(obj) : null; + protected readonly ObjectReference _obj; + public IObjectReference ObjRef { get => _obj; } + public IntPtr ThisPtr => _obj.ThisPtr; + + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + public IGraphicsEffectD2D1Interop(IObjectReference obj) : this(obj.As()) { } + internal IGraphicsEffectD2D1Interop(ObjectReference obj) + { + _obj = obj; + } + + public Guid EffectId + { + get + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetEffectId(ThisPtr, out Guid guid)); + return guid; + } + } + + public uint PropertyCount + { + get + { + unsafe + { + uint count = default; + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetPropertyCount(ThisPtr, &count)); + return count; + } + } + } + + public uint SourceCount + { + get + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetSourceCount(ThisPtr, out uint count)); + return count; + } + } + + public uint GetNamedPropertyMapping(string name, out global::Windows.Graphics.Effects.Interop.GRAPHICS_EFFECT_PROPERTY_MAPPING mapping) + { + throw new NotImplementedException(); + } + + public object GetProperty(uint index) + { + // Marshal.ThrowExceptionForHR(_obj.Vftbl.GetProperty(ThisPtr, index, out IntPtr value)); + throw new NotImplementedException(); + } + + public global::Windows.Graphics.Effects.IGraphicsEffectSource GetSource(uint index) + { + throw new NotImplementedException(); + } + } +} + diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 36398eb810..ab07bdebb3 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -599,6 +599,8 @@ namespace Avalonia.Win32 _scaling = dpix / 96.0; } } + + CompositionHost.Instance.Initialize(_hwnd); } private void CreateDropTarget() From bd2b283ad7d4df2e2088916823b174e6d37d9d24 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 Jun 2020 18:30:43 -0300 Subject: [PATCH 003/446] add drawing surface interop.. --- .../Composition/CompositionHost.cs | 21 ++- .../ICompositionDrawingSurfaceInterop.cs | 135 ++++++++++++++++++ .../IGraphicsEffectD2D1Interop1.cs | 2 +- 3 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 223c208829..a94fd82e1a 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using Avalonia.OpenGL.Angle; using Windows.UI.Composition; +using Windows.UI.Composition.Interop; using WinRT; namespace Avalonia.Win32 @@ -89,8 +90,24 @@ namespace Avalonia.Win32 var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); - gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100,100), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - + var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100,100), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); + + var surfaceInterop = surface.As(); + + surfaceInterop.BeginDraw(new Windows.Foundation.Rect(0, 0, 100, 100), Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"), out var texture, new Windows.Foundation.Point(0, 0)); + surfaceInterop.EndDraw(); + + var brush = _compositor.CreateSurfaceBrush(surface); + + var visual = _compositor.CreateSpriteVisual(); + + visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); + visual.Brush = brush; + + _target.Root = visual; + + AddElement(100, 200, 200); + } public void CreateBlur() diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs new file mode 100644 index 0000000000..fd9387b59e --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs @@ -0,0 +1,135 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; +using WinRT; + +namespace Windows.UI.Composition.Interop +{ + [WindowsRuntimeType] + [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] + public interface ICompositionDrawingSurfaceInterop + { + void BeginDraw(Rect updateRect, Guid iid, out IntPtr updateObject, Point point); + + void EndDraw(); + + void Resize(Size sizePixels); + + void ResumeDraw(); + + void Scroll(Rect scrollRect, Rect clipRect, int offsetX, int offsetY); + + void SuspendDraw(); + } +} + +namespace ABI.Windows.UI.Composition.Interop +{ + using global::System; + using global::System.Runtime.InteropServices; + using global::Windows.UI.Composition; + + [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] + internal class ICompositionDrawingSurfaceInterop : global::Windows.UI.Composition.Interop.ICompositionDrawingSurfaceInterop + + { + [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] + public struct Vftbl + { + public delegate int _BeginDraw(IntPtr ThisPtr, Guid iid, out IntPtr updateObject, Point updateOffset); + public delegate int _EndDraw(IntPtr ThisPtr); + public delegate int _Resize(IntPtr ThisPtr, Size sizePixels); + public delegate int _ResumeDraw(IntPtr ThisPtr); + public delegate int _Scroll(IntPtr ThisPtr, Rect scrollRect, Rect clipRect, int offsetX, int offsetY); + public delegate int _SuspendDraw(IntPtr ThisPtr); + + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public _BeginDraw BeginDraw; + public _EndDraw EndDraw; + public _Resize Resize; + public _ResumeDraw ResumeDraw; + public _Scroll Scroll; + public _SuspendDraw SuspendDraw; + + public static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + + BeginDraw = Do_Abi_BeginDraw, + EndDraw = Do_Abi_EndDraw, + Resize = Do_Abi_Resize + + + }; + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); + } + + private static int Do_Abi_BeginDraw(IntPtr ThisPtr, Guid iid, out IntPtr updateObject, Point updateOffset) + { + updateObject = IntPtr.Zero; + return 0; + } + + private static int Do_Abi_EndDraw(IntPtr ThisPtr) + { + return 0; + } + + private static int Do_Abi_Resize(IntPtr ThisPtr, Size sizePixels) + { + return 0; + } + } + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator ICompositionDrawingSurfaceInterop(IObjectReference obj) => (obj != null) ? new ICompositionDrawingSurfaceInterop(obj) : null; + protected readonly ObjectReference _obj; + public IObjectReference ObjRef { get => _obj; } + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + + public ICompositionDrawingSurfaceInterop(IObjectReference obj) : this(obj.As()) { } + internal ICompositionDrawingSurfaceInterop(ObjectReference obj) + { + _obj = obj; + } + + public void BeginDraw(Rect updateRect, Guid iid, out IntPtr updateObject, Point point) + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, iid, out updateObject, point)); + } + + public void EndDraw() + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.EndDraw(ThisPtr)); + } + + public void Resize(Size sizePixels) + { + throw new NotImplementedException(); + } + + public void ResumeDraw() + { + throw new NotImplementedException(); + } + + public void Scroll(Rect scrollRect, Rect clipRect, int offsetX, int offsetY) + { + throw new NotImplementedException(); + } + + public void SuspendDraw() + { + throw new NotImplementedException(); + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs index 9d053c9e22..8466b05fb5 100644 --- a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs +++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs @@ -122,7 +122,7 @@ namespace ABI.Windows.Graphics.Effects.Interop value = default; try - { + { var source = ComWrappersSupport.FindObject(thisPtr).GetSource(index); value = MarshalInterface.FromManaged(source); From 016935a0235844b0d0b1fed42735c26f22060b72 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 11:54:23 -0300 Subject: [PATCH 004/446] attached composition gl surface to composition tree. --- .../Composition/CompositionHost.cs | 67 +++++++++++++++++-- src/Windows/Avalonia.Win32/WindowImpl.cs | 11 +-- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index a94fd82e1a..961c3b0319 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using Avalonia.OpenGL; using Avalonia.OpenGL.Angle; using Windows.UI.Composition; using Windows.UI.Composition.Interop; @@ -7,6 +8,61 @@ using WinRT; namespace Avalonia.Win32 { + public class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase + { + private readonly EglDisplay _display; + private readonly EglContext _context; + private readonly IEglWindowGlPlatformSurfaceInfo _info; + private ICompositionDrawingSurfaceInterop _surfaceInterop; + + public CompositionEglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base() + { + _display = context.Display; + _context = context; + _info = info; + } + + public void AttachToCompositionTree(IntPtr hwnd) + { + _surfaceInterop = CompositionHost.Instance.Initialize(hwnd); + } + + public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() + { + var glSurface = _display.CreateWindowSurface(_info.Handle); + return new CompositionRenderTarget(_display, _context, glSurface, _surfaceInterop, _info); + } + + class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase + { + private readonly EglDisplay _display; + private readonly EglContext _context; + private readonly EglSurface _glSurface; + private readonly IEglWindowGlPlatformSurfaceInfo _info; + private PixelSize _initialSize; + private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; + + public CompositionRenderTarget(EglDisplay display, EglContext context, + EglSurface glSurface, ICompositionDrawingSurfaceInterop interopSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) + { + _display = display; + _context = context; + _glSurface = glSurface; + _surfaceInterop = interopSurface; + _info = info; + _initialSize = info.Size; + } + + public override void Dispose() => _glSurface.Dispose(); + + public override bool IsCorrupted => _initialSize != _info.Size; + + public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info); + } + } + + + class CompositionHost { internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE @@ -75,7 +131,7 @@ namespace Avalonia.Win32 } } - public void Initialize(IntPtr hwnd) + public ICompositionDrawingSurfaceInterop Initialize(IntPtr hwnd) { EnsureDispatcherQueue(); if (_dispatcherQueueController != null) @@ -90,13 +146,13 @@ namespace Avalonia.Win32 var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); - var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100,100), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); + var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100, 100), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); var surfaceInterop = surface.As(); - surfaceInterop.BeginDraw(new Windows.Foundation.Rect(0, 0, 100, 100), Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"), out var texture, new Windows.Foundation.Point(0, 0)); - surfaceInterop.EndDraw(); - + //surfaceInterop.BeginDraw(new Windows.Foundation.Rect(0, 0, 100, 100), Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"), out var texture, new Windows.Foundation.Point(0, 0)); + //surfaceInterop.EndDraw(); + var brush = _compositor.CreateSurfaceBrush(surface); var visual = _compositor.CreateSpriteVisual(); @@ -108,6 +164,7 @@ namespace Avalonia.Win32 AddElement(100, 200, 200); + return surfaceInterop; } public void CreateBlur() diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index ab07bdebb3..03d2a328a6 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -100,7 +100,12 @@ namespace Avalonia.Win32 _framebuffer = new FramebufferManager(_hwnd); if (Win32GlManager.EglFeature != null) - _gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); + _gl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); + + if (_gl is CompositionEglGlPlatformSurface cgl) + { + cgl.AttachToCompositionTree(_hwnd); + } Screen = new ScreenImpl(); @@ -598,9 +603,7 @@ namespace Avalonia.Win32 { _scaling = dpix / 96.0; } - } - - CompositionHost.Instance.Initialize(_hwnd); + } } private void CreateDropTarget() From 203e346a4d855dba07d64173844434fa31fdd1f1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 12:49:48 -0300 Subject: [PATCH 005/446] make begindraw work. --- .../Composition/CompositionHost.cs | 96 +++++++++++++++---- .../ICompositionDrawingSurfaceInterop.cs | 45 ++++++--- 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 961c3b0319..5f20d18faf 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -28,36 +28,99 @@ namespace Avalonia.Win32 } public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() - { - var glSurface = _display.CreateWindowSurface(_info.Handle); - return new CompositionRenderTarget(_display, _context, glSurface, _surfaceInterop, _info); + { + return new CompositionRenderTarget(_display, _context, _surfaceInterop, _info); } class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase { private readonly EglDisplay _display; - private readonly EglContext _context; - private readonly EglSurface _glSurface; + private readonly EglContext _context; private readonly IEglWindowGlPlatformSurfaceInfo _info; private PixelSize _initialSize; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; - public CompositionRenderTarget(EglDisplay display, EglContext context, - EglSurface glSurface, ICompositionDrawingSurfaceInterop interopSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) + public CompositionRenderTarget(EglDisplay display, EglContext context, ICompositionDrawingSurfaceInterop interopSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) { _display = display; - _context = context; - _glSurface = glSurface; + _context = context; _surfaceInterop = interopSurface; _info = info; _initialSize = info.Size; } - public override void Dispose() => _glSurface.Dispose(); + public override bool IsCorrupted => _initialSize != _info.Size; + + public override IGlPlatformSurfaceRenderingSession BeginDraw() + { + var l = _context.Lock(); + + var iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); + var updateRect = new RECT { right = _info.Size.Width, bottom = _info.Size.Height }; + var offset = new POINT(); + + _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + _surfaceInterop.BeginDraw( + ref updateRect, + ref iid, + out IntPtr texture, ref offset); - public override bool IsCorrupted => _initialSize != _info.Size; + var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture); - public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info); + var restoreContext = _context.MakeCurrent(surface); + + return new Session(_display, _context, null, _info, l, restoreContext, () => { }, true); + } + + public override void Dispose() + { + _surfaceInterop.EndDraw(); + base.Dispose(); + } + } + + class Session : IGlPlatformSurfaceRenderingSession + { + private readonly EglContext _context; + private readonly EglSurface _glSurface; + private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; + private readonly EglDisplay _display; + private readonly IDisposable _lock; + private readonly IDisposable _restoreContext; + private readonly Action _onFinish; + + + public Session(EglDisplay display, EglContext context, + EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, + IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped) + { + IsYFlipped = isYFlipped; + _context = context; + _display = display; + _glSurface = glSurface; + _info = info; + _lock = @lock; + _restoreContext = restoreContext; + _onFinish = onFinish; + } + + public void Dispose() + { + _context.GlInterface.Flush(); + _display.EglInterface.WaitGL(); + _glSurface?.SwapBuffers(); + _display.EglInterface.WaitClient(); + _display.EglInterface.WaitGL(); + _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); + _restoreContext.Dispose(); + _lock.Dispose(); + _onFinish?.Invoke(); + } + + public IGlContext Context => _context; + public PixelSize Size => _info.Size; + public double Scaling => _info.Scaling; + public bool IsYFlipped { get; } } } @@ -146,12 +209,11 @@ namespace Avalonia.Win32 var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); - var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100, 100), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - - var surfaceInterop = surface.As(); + var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100, 100), + Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, + Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - //surfaceInterop.BeginDraw(new Windows.Foundation.Rect(0, 0, 100, 100), Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"), out var texture, new Windows.Foundation.Point(0, 0)); - //surfaceInterop.EndDraw(); + var surfaceInterop = surface.As(); var brush = _compositor.CreateSurfaceBrush(surface); diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs index fd9387b59e..8fea5f293b 100644 --- a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs +++ b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs @@ -1,23 +1,39 @@ using System; using System.Runtime.InteropServices; -using Windows.Foundation; using WinRT; namespace Windows.UI.Composition.Interop { + public struct POINT + { + public int X; + public int Y; + } + + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + + public int Width => right - left; + public int Height => bottom - top; + } + [WindowsRuntimeType] [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] public interface ICompositionDrawingSurfaceInterop { - void BeginDraw(Rect updateRect, Guid iid, out IntPtr updateObject, Point point); + void BeginDraw(ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point); void EndDraw(); - void Resize(Size sizePixels); + void Resize(POINT sizePixels); void ResumeDraw(); - void Scroll(Rect scrollRect, Rect clipRect, int offsetX, int offsetY); + void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY); void SuspendDraw(); } @@ -28,6 +44,7 @@ namespace ABI.Windows.UI.Composition.Interop using global::System; using global::System.Runtime.InteropServices; using global::Windows.UI.Composition; + using global::Windows.UI.Composition.Interop; [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] internal class ICompositionDrawingSurfaceInterop : global::Windows.UI.Composition.Interop.ICompositionDrawingSurfaceInterop @@ -36,11 +53,11 @@ namespace ABI.Windows.UI.Composition.Interop [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] public struct Vftbl { - public delegate int _BeginDraw(IntPtr ThisPtr, Guid iid, out IntPtr updateObject, Point updateOffset); + public delegate int _BeginDraw(IntPtr ThisPtr, ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset); public delegate int _EndDraw(IntPtr ThisPtr); - public delegate int _Resize(IntPtr ThisPtr, Size sizePixels); + public delegate int _Resize(IntPtr ThisPtr, POINT sizePixels); public delegate int _ResumeDraw(IntPtr ThisPtr); - public delegate int _Scroll(IntPtr ThisPtr, Rect scrollRect, Rect clipRect, int offsetX, int offsetY); + public delegate int _Scroll(IntPtr ThisPtr, RECT scrollRect, RECT clipRect, int offsetX, int offsetY); public delegate int _SuspendDraw(IntPtr ThisPtr); internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; @@ -70,7 +87,7 @@ namespace ABI.Windows.UI.Composition.Interop Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); } - private static int Do_Abi_BeginDraw(IntPtr ThisPtr, Guid iid, out IntPtr updateObject, Point updateOffset) + private static int Do_Abi_BeginDraw(IntPtr ThisPtr, ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset) { updateObject = IntPtr.Zero; return 0; @@ -81,7 +98,7 @@ namespace ABI.Windows.UI.Composition.Interop return 0; } - private static int Do_Abi_Resize(IntPtr ThisPtr, Size sizePixels) + private static int Do_Abi_Resize(IntPtr ThisPtr, POINT sizePixels) { return 0; } @@ -101,9 +118,9 @@ namespace ABI.Windows.UI.Composition.Interop _obj = obj; } - public void BeginDraw(Rect updateRect, Guid iid, out IntPtr updateObject, Point point) + public void BeginDraw(ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, iid, out updateObject, point)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, ref updateRect, ref iid, out updateObject, ref point)); } public void EndDraw() @@ -111,9 +128,9 @@ namespace ABI.Windows.UI.Composition.Interop Marshal.ThrowExceptionForHR(_obj.Vftbl.EndDraw(ThisPtr)); } - public void Resize(Size sizePixels) + public void Resize(POINT sizePixels) { - throw new NotImplementedException(); + Marshal.ThrowExceptionForHR(_obj.Vftbl.Resize(ThisPtr, sizePixels)); } public void ResumeDraw() @@ -121,7 +138,7 @@ namespace ABI.Windows.UI.Composition.Interop throw new NotImplementedException(); } - public void Scroll(Rect scrollRect, Rect clipRect, int offsetX, int offsetY) + public void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY) { throw new NotImplementedException(); } From 6adb1d062475ec8679ef01048bba24902c2c95b8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 12:51:37 -0300 Subject: [PATCH 006/446] pass in wrapped surface to session. --- src/Windows/Avalonia.Win32/Composition/CompositionHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 5f20d18faf..02291d93ed 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -69,7 +69,7 @@ namespace Avalonia.Win32 var restoreContext = _context.MakeCurrent(surface); - return new Session(_display, _context, null, _info, l, restoreContext, () => { }, true); + return new Session(_display, _context, surface, _info, l, restoreContext, () => { }, true); } public override void Dispose() @@ -108,7 +108,7 @@ namespace Avalonia.Win32 { _context.GlInterface.Flush(); _display.EglInterface.WaitGL(); - _glSurface?.SwapBuffers(); + _glSurface.SwapBuffers(); _display.EglInterface.WaitClient(); _display.EglInterface.WaitGL(); _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); From f5dd50e8ea855a8e903af1e872648b60c5e7de52 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 12:56:46 -0300 Subject: [PATCH 007/446] call enddraw. --- src/Windows/Avalonia.Win32/Composition/CompositionHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 02291d93ed..b82fbc7d1d 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -69,7 +69,7 @@ namespace Avalonia.Win32 var restoreContext = _context.MakeCurrent(surface); - return new Session(_display, _context, surface, _info, l, restoreContext, () => { }, true); + return new Session(_display, _context, surface, _info, l, restoreContext, () => { _surfaceInterop.EndDraw(); }, true); } public override void Dispose() From f0c2ff7394715407368979f5361f7746a032099e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 13:02:03 -0300 Subject: [PATCH 008/446] call base.BeginDraw we dont need our own session class. --- .../Composition/CompositionHost.cs | 52 +------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index b82fbc7d1d..3837134643 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -69,58 +69,8 @@ namespace Avalonia.Win32 var restoreContext = _context.MakeCurrent(surface); - return new Session(_display, _context, surface, _info, l, restoreContext, () => { _surfaceInterop.EndDraw(); }, true); + return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); }, true); } - - public override void Dispose() - { - _surfaceInterop.EndDraw(); - base.Dispose(); - } - } - - class Session : IGlPlatformSurfaceRenderingSession - { - private readonly EglContext _context; - private readonly EglSurface _glSurface; - private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; - private readonly EglDisplay _display; - private readonly IDisposable _lock; - private readonly IDisposable _restoreContext; - private readonly Action _onFinish; - - - public Session(EglDisplay display, EglContext context, - EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, - IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped) - { - IsYFlipped = isYFlipped; - _context = context; - _display = display; - _glSurface = glSurface; - _info = info; - _lock = @lock; - _restoreContext = restoreContext; - _onFinish = onFinish; - } - - public void Dispose() - { - _context.GlInterface.Flush(); - _display.EglInterface.WaitGL(); - _glSurface.SwapBuffers(); - _display.EglInterface.WaitClient(); - _display.EglInterface.WaitGL(); - _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); - _restoreContext.Dispose(); - _lock.Dispose(); - _onFinish?.Invoke(); - } - - public IGlContext Context => _context; - public PixelSize Size => _info.Size; - public double Scaling => _info.Scaling; - public bool IsYFlipped { get; } } } From 53f3bc6e7fe84fc0fb3bf9e5f11fd327664ff9ca Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 13:25:24 -0300 Subject: [PATCH 009/446] working rendering. --- .../Composition/CompositionHost.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 3837134643..d4bbb67a3b 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -28,14 +28,14 @@ namespace Avalonia.Win32 } public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() - { + { return new CompositionRenderTarget(_display, _context, _surfaceInterop, _info); } class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase { private readonly EglDisplay _display; - private readonly EglContext _context; + private readonly EglContext _context; private readonly IEglWindowGlPlatformSurfaceInfo _info; private PixelSize _initialSize; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; @@ -43,7 +43,7 @@ namespace Avalonia.Win32 public CompositionRenderTarget(EglDisplay display, EglContext context, ICompositionDrawingSurfaceInterop interopSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) { _display = display; - _context = context; + _context = context; _surfaceInterop = interopSurface; _info = info; _initialSize = info.Size; @@ -53,8 +53,6 @@ namespace Avalonia.Win32 public override IGlPlatformSurfaceRenderingSession BeginDraw() { - var l = _context.Lock(); - var iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); var updateRect = new RECT { right = _info.Size.Width, bottom = _info.Size.Height }; var offset = new POINT(); @@ -67,8 +65,6 @@ namespace Avalonia.Win32 var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture); - var restoreContext = _context.MakeCurrent(surface); - return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); }, true); } } @@ -159,11 +155,11 @@ namespace Avalonia.Win32 var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); - var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100, 100), - Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, + var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100, 100), + Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - var surfaceInterop = surface.As(); + var surfaceInterop = surface.As(); var brush = _compositor.CreateSurfaceBrush(surface); @@ -174,7 +170,7 @@ namespace Avalonia.Win32 _target.Root = visual; - AddElement(100, 200, 200); + //CreateBlur(); return surfaceInterop; } From e53dfc0d360cb1c34706ae81b54bda7aed65b4c2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 14:02:51 -0300 Subject: [PATCH 010/446] add blur and avalonia surface visual ontop of blur. --- samples/ControlCatalog/MainWindow.xaml | 3 ++- .../Avalonia.Win32/Composition/CompositionHost.cs | 11 +++++++---- .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 1 + src/Windows/Avalonia.Win32/WindowImpl.cs | 4 ++++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 76422bc130..c34e392d40 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -7,7 +7,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ControlCatalog.ViewModels" xmlns:v="clr-namespace:ControlCatalog.Views" - x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}"> + TransparencyLevelHint="Transparent" + x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" Background="{x:Null}"> diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index d4bbb67a3b..874bdaaf69 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -155,7 +155,7 @@ namespace Avalonia.Win32 var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); - var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(100, 100), + var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); @@ -167,10 +167,13 @@ namespace Avalonia.Win32 visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); visual.Brush = brush; + //_target.Root = visual; - _target.Root = visual; + CreateBlur(); + + var visuals = _target.Root.As().Children; - //CreateBlur(); + visuals.InsertAtTop(visual); return surfaceInterop; } @@ -196,7 +199,7 @@ namespace Avalonia.Win32 void CreateCompositionRoot() { var root = _compositor.CreateContainerVisual(); - root.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); + root.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); //root.Offset = new System.Numerics.Vector3(0, 0, 0); _target.Root = root; } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index b3b38db1ab..bef65a06d8 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -465,6 +465,7 @@ namespace Avalonia.Win32.Interop WS_VSCROLL = 0x200000, WS_EX_DLGMODALFRAME = 0x00000001, WS_EX_NOPARENTNOTIFY = 0x00000004, + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, WS_EX_TOPMOST = 0x00000008, WS_EX_ACCEPTFILES = 0x00000010, WS_EX_TRANSPARENT = 0x00000020, diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 03d2a328a6..fdde128800 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -106,6 +106,10 @@ namespace Avalonia.Win32 { cgl.AttachToCompositionTree(_hwnd); } + else + { + CompositionHost.Instance.Initialize(_hwnd); + } Screen = new ScreenImpl(); From 1f9e395fabb55234e452cdfe94562774e950e61a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 14:53:05 -0300 Subject: [PATCH 011/446] hack to clear window context, and only call interopsurface.resize when size actually changes. --- .../Composition/CompositionHost.cs | 33 +++++++++++++++++-- .../Composition/GaussianBlurEffect.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 ++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 874bdaaf69..fd76e4a523 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -51,13 +51,42 @@ namespace Avalonia.Win32 public override bool IsCorrupted => _initialSize != _info.Size; + bool _firstRun = true; + POINT lastSize; public override IGlPlatformSurfaceRenderingSession BeginDraw() { + if (_firstRun) + { + _firstRun = false; + var windowSurface = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, _info); + + using (var target = windowSurface.CreateGlRenderTarget()) + { + using (var session = target.BeginDraw()) + { + using (session.Context.MakeCurrent()) + { + var gl = _context.GlInterface; + gl.Viewport(0, 0, _info.Size.Width, _info.Size.Height); + gl.ClearStencil(0); + gl.ClearColor(0, 0, 0, 0); + gl.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_DEPTH_BUFFER_BIT); + gl.Flush(); + } + } + } + } + + var iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); var updateRect = new RECT { right = _info.Size.Width, bottom = _info.Size.Height }; var offset = new POINT(); - _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + if (lastSize.X != _info.Size.Width || lastSize.Y != _info.Size.Height) + { + lastSize = new POINT { X = _info.Size.Width, Y = _info.Size.Height }; + _surfaceInterop.Resize(lastSize); + } _surfaceInterop.BeginDraw( ref updateRect, ref iid, @@ -65,7 +94,7 @@ namespace Avalonia.Win32 var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture); - return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); }, true); + return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); } } } diff --git a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs index 19595e8977..4e1be3af1a 100644 --- a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs +++ b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs @@ -52,7 +52,7 @@ namespace Avalonia.Win32 return 30.0f; case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION: - return (UInt32)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED; + return (UInt32)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED; case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE: return (UInt32)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index fdde128800..151a998673 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -100,7 +100,9 @@ namespace Avalonia.Win32 _framebuffer = new FramebufferManager(_hwnd); if (Win32GlManager.EglFeature != null) + { _gl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); + } if (_gl is CompositionEglGlPlatformSurface cgl) { From 372f8e3938088ed882f006e32e1d7b083149a145 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Jun 2020 15:29:35 -0300 Subject: [PATCH 012/446] 1 graphics device, 1 surface and visual tree per window. --- .../Composition/CompositionHost.cs | 49 +++++++++---------- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index fd76e4a523..1735b22035 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -24,7 +24,7 @@ namespace Avalonia.Win32 public void AttachToCompositionTree(IntPtr hwnd) { - _surfaceInterop = CompositionHost.Instance.Initialize(hwnd); + _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd); } public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() @@ -47,6 +47,8 @@ namespace Avalonia.Win32 _surfaceInterop = interopSurface; _info = info; _initialSize = info.Size; + lastSize = new POINT { X = _info.Size.Width, Y = _info.Size.Height }; + _surfaceInterop.Resize(lastSize); } public override bool IsCorrupted => _initialSize != _info.Size; @@ -85,7 +87,7 @@ namespace Avalonia.Win32 if (lastSize.X != _info.Size.Width || lastSize.Y != _info.Size.Height) { lastSize = new POINT { X = _info.Size.Width, Y = _info.Size.Height }; - _surfaceInterop.Resize(lastSize); + // _surfaceInterop.Resize(lastSize); } _surfaceInterop.BeginDraw( ref updateRect, @@ -135,17 +137,18 @@ namespace Avalonia.Win32 private Compositor _compositor; private Windows.System.DispatcherQueueController _dispatcherQueueController; - private Windows.UI.Composition.Desktop.DesktopWindowTarget _target; + private CompositionGraphicsDevice _graphicsDevice; private CompositionHost() { + Initialize(); } - public void AddElement(float size, float x, float y) + public void AddElement(CompositionTarget target, float size, float x, float y) { - if (_target.Root != null) + if (target.Root != null) { - var visuals = _target.Root.As().Children; + var visuals = target.Root.As().Children; var visual = _compositor.CreateSpriteVisual(); @@ -169,22 +172,24 @@ namespace Avalonia.Win32 } } - public ICompositionDrawingSurfaceInterop Initialize(IntPtr hwnd) + private void Initialize() { EnsureDispatcherQueue(); if (_dispatcherQueueController != null) _compositor = new Windows.UI.Composition.Compositor(); - CreateDesktopWindowTarget(hwnd); - CreateCompositionRoot(); - var interop = _compositor.As(); var display = Win32GlManager.EglFeature.Display as AngleWin32EglDisplay; - var gDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); + _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); + } + + public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd) + { + var target = CreateDesktopWindowTarget(hwnd); - var surface = gDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0), + var surface = _graphicsDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); @@ -198,16 +203,16 @@ namespace Avalonia.Win32 visual.Brush = brush; //_target.Root = visual; - CreateBlur(); + target.Root = CreateBlur(); - var visuals = _target.Root.As().Children; + var visuals = target.Root.As().Children; visuals.InsertAtTop(visual); return surfaceInterop; } - public void CreateBlur() + public SpriteVisual CreateBlur() { var effect = new GaussianBlurEffect(); var effectFactory = _compositor.CreateEffectFactory(effect); @@ -222,23 +227,15 @@ namespace Avalonia.Win32 visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); visual.Brush = blurBrush; - _target.Root = visual; - } - - void CreateCompositionRoot() - { - var root = _compositor.CreateContainerVisual(); - root.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); - //root.Offset = new System.Numerics.Vector3(0, 0, 0); - _target.Root = root; + return visual; } - void CreateDesktopWindowTarget(IntPtr window) + CompositionTarget CreateDesktopWindowTarget(IntPtr window) { var interop = _compositor.As(); interop.CreateDesktopWindowTarget(window, false, out var windowTarget); - _target = Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); + return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); } void EnsureDispatcherQueue() diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 151a998673..bcccdf4810 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -110,7 +110,7 @@ namespace Avalonia.Win32 } else { - CompositionHost.Instance.Initialize(_hwnd); + CompositionHost.Instance.InitialiseWindowCompositionTree(_hwnd); } Screen = new ScreenImpl(); From c22b71aa50cc0b74848be1db1723376a736f7b3a Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 6 Aug 2020 17:41:32 +0800 Subject: [PATCH 013/446] update build.ps1 to put downloaded dotnet in PATH --- build.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.ps1 b/build.ps1 index 57e2f80075..3672e82d3b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -62,6 +62,8 @@ else { } else { ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } + + $env:PATH="$DotNetDirectory;$env:PATH" } Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" From 7fa2c8495690b0af1699b87ed62cd273fbd91e59 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Fri, 31 Jul 2020 20:28:04 +0300 Subject: [PATCH 014/446] Add handling mouse events in webapp --- .../webapp/src/FramePresenter.tsx | 25 ++++++++++++- .../src/Models/Input/InputEventMessageBase.ts | 11 ++++++ .../webapp/src/Models/Input/InputModifiers.ts | 9 +++++ .../webapp/src/Models/Input/MouseButton.ts | 6 +++ .../src/Models/Input/MouseEventHelpers.ts | 37 +++++++++++++++++++ .../Models/Input/PointerEventMessageBase.ts | 13 +++++++ .../Models/Input/PointerMovedEventMessage.ts | 12 ++++++ .../Input/PointerPressedEventMessage.ts | 17 +++++++++ .../Input/PointerReleasedEventMessage.ts | 17 +++++++++ 9 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx index 0059cfe683..ce43822dd8 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx @@ -1,5 +1,8 @@ -import {PreviewerFrame, PreviewerServerConnection} from "src/PreviewerServerConnection"; import * as React from "react"; +import {PreviewerFrame, PreviewerServerConnection} from "src/PreviewerServerConnection"; +import {PointerPressedEventMessage} from "src/Models/Input/PointerPressedEventMessage"; +import {PointerReleasedEventMessage} from "src/Models/Input/PointerReleasedEventMessage"; +import {PointerMovedEventMessage} from "src/Models/Input/PointerMovedEventMessage"; interface PreviewerPresenterProps { conn: PreviewerServerConnection; @@ -51,7 +54,25 @@ export class PreviewerPresenter extends React.Component } } + handleMouseDown(e: React.MouseEvent) { + const pointerPressedEventMessage = new PointerPressedEventMessage(e); + // TODO: Send message to server + } + + handleMouseUp(e: React.MouseEvent) { + const pointerReleasedEventMessage = new PointerReleasedEventMessage(e); + // TODO: Send message to server + } + + handleMouseMove(e: React.MouseEvent) { + const pointerMovedEventMessage = new PointerMovedEventMessage(e); + // TODO: Send message to server + } + render() { - return + return } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts new file mode 100644 index 0000000000..2da7424e2a --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts @@ -0,0 +1,11 @@ +import * as React from "react"; +import {InputModifiers} from "src/Models/Input/InputModifiers"; +import {getModifiers} from "src/Models/Input/MouseEventHelpers"; + +export abstract class InputEventMessageBase { + public readonly modifiers : Array; + + protected constructor(e: React.MouseEvent) { + this.modifiers = getModifiers(e); + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers.ts new file mode 100644 index 0000000000..483b4c02d0 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers.ts @@ -0,0 +1,9 @@ +export enum InputModifiers { + Alt, + Control, + Shift, + Windows, + LeftMouseButton, + RightMouseButton, + MiddleMouseButton, +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton.ts new file mode 100644 index 0000000000..880f8fb5ce --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton.ts @@ -0,0 +1,6 @@ +export enum MouseButton { + None, + Left, + Right, + Middle, +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts new file mode 100644 index 0000000000..783bacad38 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts @@ -0,0 +1,37 @@ +import * as React from "react"; +import {InputModifiers} from "src/Models/Input/InputModifiers"; +import {MouseButton} from "src/Models/Input/MouseButton"; + +export function getModifiers(e: React.MouseEvent): Array { + + let modifiers : Array = []; + + if (e.altKey) + modifiers.push(InputModifiers.Alt); + if (e.ctrlKey) + modifiers.push(InputModifiers.Control); + if (e.shiftKey) + modifiers.push(InputModifiers.Shift); + if (e.metaKey) + modifiers.push(InputModifiers.Windows); + if ((e.buttons & 1) != 0) + modifiers.push(InputModifiers.LeftMouseButton); + if ((e.buttons & 2) != 0) + modifiers.push(InputModifiers.RightMouseButton); + if ((e.buttons & 4) != 0) + modifiers.push(InputModifiers.MiddleMouseButton); + + return modifiers; +} + +export function getMouseButton(e: React.MouseEvent) : MouseButton { + if (e.button == 1) { + return MouseButton.Left; + } else if (e.button == 2) { + return MouseButton.Right; + } else if (e.button == 4) { + return MouseButton.Middle + } else { + return MouseButton.None; + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts new file mode 100644 index 0000000000..e43362ac1c --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts @@ -0,0 +1,13 @@ +import * as React from "react"; +import {InputEventMessageBase} from "src/Models/Input/InputEventMessageBase"; + +export abstract class PointerEventMessageBase extends InputEventMessageBase { + public readonly x: number; + public readonly y: number; + + protected constructor(e: React.MouseEvent) { + super(e) + this.x = e.clientX; + this.y = e.clientY; + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts new file mode 100644 index 0000000000..3ca8e91b8d --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts @@ -0,0 +1,12 @@ +import * as React from "react"; +import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; + +export class PointerMovedEventMessage extends PointerEventMessageBase { + constructor(e: React.MouseEvent) { + super(e) + } + + public toString = () : string => { + return `pointer-moved:${this.modifiers}:${this.x}:${this.y}`; + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts new file mode 100644 index 0000000000..712ec64a14 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts @@ -0,0 +1,17 @@ +import * as React from "react"; +import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; +import {MouseButton} from "src/Models/Input/MouseButton"; +import {getMouseButton} from "src/Models/Input/MouseEventHelpers"; + +export class PointerPressedEventMessage extends PointerEventMessageBase { + public readonly button: MouseButton + + constructor(e: React.MouseEvent) { + super(e) + this.button = getMouseButton(e); + } + + public toString = () : string => { + return `pointer-pressed:${this.modifiers}:${this.button}:${this.x}:${this.y}`; + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts new file mode 100644 index 0000000000..46234263db --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts @@ -0,0 +1,17 @@ +import * as React from "react"; +import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; +import {MouseButton} from "src/Models/Input/MouseButton"; +import {getMouseButton} from "src/Models/Input/MouseEventHelpers"; + +export class PointerReleasedEventMessage extends PointerEventMessageBase { + public readonly button: MouseButton + + constructor(e: React.MouseEvent) { + super(e) + this.button = getMouseButton(e); + } + + public toString = () : string => { + return `pointer-released:${this.modifiers}:${this.button}:${this.x}:${this.y}`; + } +} From 23d29d445c316df9718d598f5439951d2fb993c7 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Sun, 9 Aug 2020 19:54:13 +0300 Subject: [PATCH 015/446] Add message sending and server processing --- .../Remote/HtmlTransport/HtmlTransport.cs | 71 +++++++++++++++++-- .../webapp/src/FramePresenter.tsx | 10 ++- .../webapp/src/PreviewerServerConnection.ts | 6 ++ 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 55e2df8890..eb64f83d35 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Viewport; +using InputProtocol = Avalonia.Remote.Protocol.Input; namespace Avalonia.DesignerSupport.Remote.HtmlTransport { @@ -117,10 +118,55 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport return; if (msg.IsText) { - var s = Encoding.UTF8.GetString(msg.Data); - var parts = s.Split(':'); - if (parts[0] == "frame-received") - _onMessage?.Invoke(this, new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }); + var parts = msg.AsString().Split(':'); + switch (parts[0]) + { + case "frame-received": + { + _onMessage?.Invoke( + this, + new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }); + break; + } + case "pointer-released": + { + _onMessage?.Invoke( + this, + new InputProtocol.PointerReleasedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + Button = ParseMouseButton(parts[2]), + X = double.Parse(parts[3]), + Y = double.Parse(parts[4]), + }); + break; + } + case "pointer-pressed": + { + _onMessage?.Invoke( + this, + new InputProtocol.PointerPressedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + Button = ParseMouseButton(parts[2]), + X = double.Parse(parts[3]), + Y = double.Parse(parts[4]), + }); + break; + } + case "pointer-moved": + { + _onMessage?.Invoke( + this, + new InputProtocol.PointerMovedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + }); + break; + } + } } } } @@ -262,5 +308,22 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport _onException?.Invoke(this, ex); } #endregion + + private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) + { + var enumTexts = modifiersText.Split(','); + if (string.IsNullOrEmpty(enumTexts[0])) + return new InputProtocol.InputModifiers[0]; + return enumTexts + .Select(x => (InputProtocol.InputModifiers)Enum.Parse( + typeof(InputProtocol.InputModifiers), x)) + .ToArray(); + } + + private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => + string.IsNullOrEmpty(buttonText) + ? InputProtocol.MouseButton.None + : (InputProtocol.MouseButton)Enum.Parse( + typeof(InputProtocol.MouseButton), buttonText); } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx index ce43822dd8..b35e611c95 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx @@ -18,6 +18,10 @@ export class PreviewerPresenter extends React.Component this.componentDidUpdate({ conn: null! }, this.state); + + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleMouseUp = this.handleMouseUp.bind(this); + this.handleMouseMove = this.handleMouseMove.bind(this); } componentDidMount(): void { @@ -56,17 +60,17 @@ export class PreviewerPresenter extends React.Component handleMouseDown(e: React.MouseEvent) { const pointerPressedEventMessage = new PointerPressedEventMessage(e); - // TODO: Send message to server + this.props.conn.sendMouseEvent(pointerPressedEventMessage); } handleMouseUp(e: React.MouseEvent) { const pointerReleasedEventMessage = new PointerReleasedEventMessage(e); - // TODO: Send message to server + this.props.conn.sendMouseEvent(pointerReleasedEventMessage); } handleMouseMove(e: React.MouseEvent) { const pointerMovedEventMessage = new PointerMovedEventMessage(e); - // TODO: Send message to server + this.props.conn.sendMouseEvent(pointerMovedEventMessage); } render() { diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts index 891c5094e7..7f1ab84f99 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts @@ -1,3 +1,5 @@ +import { InputEventMessageBase } from "src/Models/Input/InputEventMessageBase"; + export interface PreviewerFrame { data: ImageData; dpiX: number; @@ -28,6 +30,10 @@ export class PreviewerServerConnection { this.handlers.delete(listener); } + public sendMouseEvent(message: InputEventMessageBase) { + this.conn.send(message.toString()); + } + constructor(uri: string) { this.currentFrame = null; var conn = this.conn = new WebSocket(uri); From 851c03fa70d53675edc2693ed23299ca8c407bef Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Mon, 10 Aug 2020 17:08:53 +0300 Subject: [PATCH 016/446] Add handling wheel --- .../Remote/HtmlTransport/HtmlTransport.cs | 26 ++++++++++++++----- .../webapp/src/FramePresenter.tsx | 11 +++++++- .../Models/Input/PointerEventMessageBase.ts | 2 +- .../Models/Input/PointerMovedEventMessage.ts | 2 +- .../Input/PointerPressedEventMessage.ts | 4 +-- .../Input/PointerReleasedEventMessage.ts | 4 +-- .../src/Models/Input/ScrollEventMessage.ts | 17 ++++++++++++ 7 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index eb64f83d35..c32aefe308 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -135,9 +135,9 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport new InputProtocol.PointerReleasedEventMessage { Modifiers = ParseInputModifiers(parts[1]), - Button = ParseMouseButton(parts[2]), - X = double.Parse(parts[3]), - Y = double.Parse(parts[4]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + Button = ParseMouseButton(parts[4]), }); break; } @@ -148,9 +148,9 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport new InputProtocol.PointerPressedEventMessage { Modifiers = ParseInputModifiers(parts[1]), - Button = ParseMouseButton(parts[2]), - X = double.Parse(parts[3]), - Y = double.Parse(parts[4]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + Button = ParseMouseButton(parts[4]), }); break; } @@ -166,6 +166,20 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport }); break; } + case "scroll": + { + _onMessage?.Invoke( + this, + new InputProtocol.ScrollEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + DeltaX = double.Parse(parts[4]), + DeltaY = double.Parse(parts[5]), + }); + break; + } } } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx index b35e611c95..5182a087e7 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx @@ -3,6 +3,7 @@ import {PreviewerFrame, PreviewerServerConnection} from "src/PreviewerServerConn import {PointerPressedEventMessage} from "src/Models/Input/PointerPressedEventMessage"; import {PointerReleasedEventMessage} from "src/Models/Input/PointerReleasedEventMessage"; import {PointerMovedEventMessage} from "src/Models/Input/PointerMovedEventMessage"; +import {ScrollEventMessage} from "src/Models/Input/ScrollEventMessage"; interface PreviewerPresenterProps { conn: PreviewerServerConnection; @@ -22,6 +23,7 @@ export class PreviewerPresenter extends React.Component this.handleMouseDown = this.handleMouseDown.bind(this); this.handleMouseUp = this.handleMouseUp.bind(this); this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleWheel = this.handleWheel.bind(this); } componentDidMount(): void { @@ -73,10 +75,17 @@ export class PreviewerPresenter extends React.Component this.props.conn.sendMouseEvent(pointerMovedEventMessage); } + handleWheel(e: React.WheelEvent) { + e.preventDefault(); + const scrollEventMessage = new ScrollEventMessage(e); + this.props.conn.sendMouseEvent(scrollEventMessage); + } + render() { return + onMouseMove={this.handleMouseMove} + onWheel={this.handleWheel} /> } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts index e43362ac1c..fc304c2965 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts @@ -6,7 +6,7 @@ export abstract class PointerEventMessageBase extends InputEventMessageBase { public readonly y: number; protected constructor(e: React.MouseEvent) { - super(e) + super(e); this.x = e.clientX; this.y = e.clientY; } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts index 3ca8e91b8d..cd135dfa39 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts @@ -3,7 +3,7 @@ import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase" export class PointerMovedEventMessage extends PointerEventMessageBase { constructor(e: React.MouseEvent) { - super(e) + super(e); } public toString = () : string => { diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts index 712ec64a14..e75551e0b3 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts @@ -7,11 +7,11 @@ export class PointerPressedEventMessage extends PointerEventMessageBase { public readonly button: MouseButton constructor(e: React.MouseEvent) { - super(e) + super(e); this.button = getMouseButton(e); } public toString = () : string => { - return `pointer-pressed:${this.modifiers}:${this.button}:${this.x}:${this.y}`; + return `pointer-pressed:${this.modifiers}:${this.x}:${this.y}:${this.button}`; } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts index 46234263db..c2bc368323 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts @@ -7,11 +7,11 @@ export class PointerReleasedEventMessage extends PointerEventMessageBase { public readonly button: MouseButton constructor(e: React.MouseEvent) { - super(e) + super(e); this.button = getMouseButton(e); } public toString = () : string => { - return `pointer-released:${this.modifiers}:${this.button}:${this.x}:${this.y}`; + return `pointer-released:${this.modifiers}:${this.x}:${this.y}:${this.button}`; } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts new file mode 100644 index 0000000000..427ccc5e5e --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts @@ -0,0 +1,17 @@ +import * as React from "react"; +import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; + +export class ScrollEventMessage extends PointerEventMessageBase { + public readonly deltaX: number; + public readonly deltaY: number; + + constructor(e: React.WheelEvent) { + super(e); + this.deltaX = -e.deltaX; + this.deltaY = -e.deltaY; + } + + public toString = () : string => { + return `scroll:${this.modifiers}:${this.x}:${this.y}:${this.deltaX}:${this.deltaY}`; + } +} From 27f289e2bffb3ad0ecbc00eb0b327e41c14d520c Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Mon, 10 Aug 2020 23:21:59 +0300 Subject: [PATCH 017/446] Refactor --- .../Remote/HtmlTransport/HtmlTransport.cs | 141 +++++++++--------- .../webapp/src/FramePresenter.tsx | 13 +- 2 files changed, 76 insertions(+), 78 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index c32aefe308..51d266f11a 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -118,69 +118,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport return; if (msg.IsText) { - var parts = msg.AsString().Split(':'); - switch (parts[0]) - { - case "frame-received": - { - _onMessage?.Invoke( - this, - new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }); - break; - } - case "pointer-released": - { - _onMessage?.Invoke( - this, - new InputProtocol.PointerReleasedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), - Button = ParseMouseButton(parts[4]), - }); - break; - } - case "pointer-pressed": - { - _onMessage?.Invoke( - this, - new InputProtocol.PointerPressedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), - Button = ParseMouseButton(parts[4]), - }); - break; - } - case "pointer-moved": - { - _onMessage?.Invoke( - this, - new InputProtocol.PointerMovedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), - }); - break; - } - case "scroll": - { - _onMessage?.Invoke( - this, - new InputProtocol.ScrollEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), - DeltaX = double.Parse(parts[4]), - DeltaY = double.Parse(parts[5]), - }); - break; - } - } + ProcessingReceiveMessage(msg.AsString()); } } } @@ -323,16 +261,81 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport } #endregion - private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) + private void ProcessingReceiveMessage(string message) { - var enumTexts = modifiersText.Split(','); - if (string.IsNullOrEmpty(enumTexts[0])) - return new InputProtocol.InputModifiers[0]; - return enumTexts + var parts = message.Split(':'); + switch (parts[0]) + { + case "frame-received": + { + _onMessage?.Invoke( + this, + new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }); + break; + } + case "pointer-released": + { + _onMessage?.Invoke( + this, + new InputProtocol.PointerReleasedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + Button = ParseMouseButton(parts[4]), + }); + break; + } + case "pointer-pressed": + { + _onMessage?.Invoke( + this, + new InputProtocol.PointerPressedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + Button = ParseMouseButton(parts[4]), + }); + break; + } + case "pointer-moved": + { + _onMessage?.Invoke( + this, + new InputProtocol.PointerMovedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + }); + break; + } + case "scroll": + { + _onMessage?.Invoke( + this, + new InputProtocol.ScrollEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = double.Parse(parts[2]), + Y = double.Parse(parts[3]), + DeltaX = double.Parse(parts[4]), + DeltaY = double.Parse(parts[5]), + }); + break; + } + } + } + + private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) => + string.IsNullOrEmpty(modifiersText) + ? new InputProtocol.InputModifiers[0] + : modifiersText + .Split(',') .Select(x => (InputProtocol.InputModifiers)Enum.Parse( typeof(InputProtocol.InputModifiers), x)) .ToArray(); - } private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrEmpty(buttonText) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx index 5182a087e7..ec8a1bab68 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx @@ -19,11 +19,6 @@ export class PreviewerPresenter extends React.Component this.componentDidUpdate({ conn: null! }, this.state); - - this.handleMouseDown = this.handleMouseDown.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.handleMouseMove = this.handleMouseMove.bind(this); - this.handleWheel = this.handleWheel.bind(this); } componentDidMount(): void { @@ -83,9 +78,9 @@ export class PreviewerPresenter extends React.Component render() { return + onMouseDown={this.handleMouseDown.bind(this)} + onMouseUp={this.handleMouseUp.bind(this)} + onMouseMove={this.handleMouseMove.bind(this)} + onWheel={this.handleWheel.bind(this)} /> } } From 162d6f8cedf4f06e49846bd6f182e86af5d5f27e Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 11 Aug 2020 00:29:33 +0300 Subject: [PATCH 018/446] Return null instead of empty array --- .../Remote/HtmlTransport/HtmlTransport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 51d266f11a..361d35c0b7 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -330,7 +330,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) => string.IsNullOrEmpty(modifiersText) - ? new InputProtocol.InputModifiers[0] + ? null : modifiersText .Split(',') .Select(x => (InputProtocol.InputModifiers)Enum.Parse( From f3718a5d034ab710e8416f9b76883f4f154c2a1b Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 11 Aug 2020 01:52:33 +0300 Subject: [PATCH 019/446] revert bind and preventdefault --- .../HtmlTransport/webapp/src/FramePresenter.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx index ec8a1bab68..96d2f45fdc 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx @@ -19,6 +19,11 @@ export class PreviewerPresenter extends React.Component this.componentDidUpdate({ conn: null! }, this.state); + + this.handleMouseDown = this.handleMouseDown.bind(this); + this.handleMouseUp = this.handleMouseUp.bind(this); + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleWheel = this.handleWheel.bind(this); } componentDidMount(): void { @@ -56,16 +61,19 @@ export class PreviewerPresenter extends React.Component } handleMouseDown(e: React.MouseEvent) { + e.preventDefault(); const pointerPressedEventMessage = new PointerPressedEventMessage(e); this.props.conn.sendMouseEvent(pointerPressedEventMessage); } handleMouseUp(e: React.MouseEvent) { + e.preventDefault(); const pointerReleasedEventMessage = new PointerReleasedEventMessage(e); this.props.conn.sendMouseEvent(pointerReleasedEventMessage); } handleMouseMove(e: React.MouseEvent) { + e.preventDefault(); const pointerMovedEventMessage = new PointerMovedEventMessage(e); this.props.conn.sendMouseEvent(pointerMovedEventMessage); } @@ -78,9 +86,9 @@ export class PreviewerPresenter extends React.Component render() { return + onMouseDown={this.handleMouseDown} + onMouseUp={this.handleMouseUp} + onMouseMove={this.handleMouseMove} + onWheel={this.handleWheel} /> } } From 1cfa116cc325461c041c2ed2c17a367a2f759795 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 11 Aug 2020 14:46:54 +0300 Subject: [PATCH 020/446] Fix typo in detect mouse button --- .../webapp/src/Models/Input/MouseEventHelpers.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts index 783bacad38..3f85959d6f 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts @@ -25,12 +25,12 @@ export function getModifiers(e: React.MouseEvent): Array { } export function getMouseButton(e: React.MouseEvent) : MouseButton { - if (e.button == 1) { + if (e.button == 0) { return MouseButton.Left; + } else if (e.button == 1) { + return MouseButton.Middle; } else if (e.button == 2) { return MouseButton.Right; - } else if (e.button == 4) { - return MouseButton.Middle } else { return MouseButton.None; } From 138404de4e0706b9e3dc4ba50b5ce296540745b3 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Sat, 29 Aug 2020 14:21:32 +0300 Subject: [PATCH 021/446] Fix double parse with InvariantCulture --- .../Remote/HtmlTransport/HtmlTransport.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 361d35c0b7..5946e2681c 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; @@ -280,8 +281,8 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport new InputProtocol.PointerReleasedEventMessage { Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), Button = ParseMouseButton(parts[4]), }); break; @@ -293,8 +294,8 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport new InputProtocol.PointerPressedEventMessage { Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), Button = ParseMouseButton(parts[4]), }); break; @@ -306,8 +307,8 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport new InputProtocol.PointerMovedEventMessage { Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), }); break; } @@ -318,10 +319,10 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport new InputProtocol.ScrollEventMessage { Modifiers = ParseInputModifiers(parts[1]), - X = double.Parse(parts[2]), - Y = double.Parse(parts[3]), - DeltaX = double.Parse(parts[4]), - DeltaY = double.Parse(parts[5]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + DeltaX = ParseDouble(parts[4]), + DeltaY = ParseDouble(parts[5]), }); break; } @@ -334,13 +335,16 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport : modifiersText .Split(',') .Select(x => (InputProtocol.InputModifiers)Enum.Parse( - typeof(InputProtocol.InputModifiers), x)) + typeof(InputProtocol.InputModifiers), x, true)) .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrEmpty(buttonText) ? InputProtocol.MouseButton.None : (InputProtocol.MouseButton)Enum.Parse( - typeof(InputProtocol.MouseButton), buttonText); + typeof(InputProtocol.MouseButton), buttonText, true); + + private static double ParseDouble(string text) => + double.Parse(text, CultureInfo.InvariantCulture); } } From 26ae25d28dee5bd278da14c3a51800b8628d6037 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Sat, 29 Aug 2020 14:35:05 +0300 Subject: [PATCH 022/446] Refactor parsing message --- .../Remote/HtmlTransport/HtmlTransport.cs | 88 ++++++++----------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 5946e2681c..ded9ad6b11 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -115,11 +115,11 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport while (true) { var msg = await socket.ReceiveMessage().ConfigureAwait(false); - if(msg == null) - return; - if (msg.IsText) + if(msg != null && msg.IsText) { - ProcessingReceiveMessage(msg.AsString()); + var message = ParseMessage(msg.AsString()); + if (message != null) + _onMessage?.Invoke(this, message); } } } @@ -178,7 +178,6 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport _pendingSocket?.Dispose(); _simpleServer.Dispose(); } - public Task Send(object data) { @@ -262,70 +261,57 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport } #endregion - private void ProcessingReceiveMessage(string message) + private static object ParseMessage(string message) { var parts = message.Split(':'); switch (parts[0]) { case "frame-received": { - _onMessage?.Invoke( - this, - new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }); - break; + return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; } case "pointer-released": { - _onMessage?.Invoke( - this, - new InputProtocol.PointerReleasedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }); - break; + return new InputProtocol.PointerReleasedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; } case "pointer-pressed": { - _onMessage?.Invoke( - this, - new InputProtocol.PointerPressedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }); - break; + return new InputProtocol.PointerPressedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; } case "pointer-moved": { - _onMessage?.Invoke( - this, - new InputProtocol.PointerMovedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - }); - break; + return new InputProtocol.PointerMovedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + }; } case "scroll": { - _onMessage?.Invoke( - this, - new InputProtocol.ScrollEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - DeltaX = ParseDouble(parts[4]), - DeltaY = ParseDouble(parts[5]), - }); - break; + return new InputProtocol.ScrollEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + DeltaX = ParseDouble(parts[4]), + DeltaY = ParseDouble(parts[5]), + }; } + default: + return null; } } @@ -345,6 +331,6 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport typeof(InputProtocol.MouseButton), buttonText, true); private static double ParseDouble(string text) => - double.Parse(text, CultureInfo.InvariantCulture); + double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); } } From cf42bc3bd0899545149d947127a1fa0bef8c97d9 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 1 Sep 2020 09:12:44 +0300 Subject: [PATCH 023/446] Fix comparing strings with InvariantCultureIgnoreCase --- .../Remote/HtmlTransport/HtmlTransport.cs | 92 +++++++++---------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index ded9ad6b11..1d7fd91756 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -264,55 +264,53 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport private static object ParseMessage(string message) { var parts = message.Split(':'); - switch (parts[0]) + var key = parts[0]; + if (key.Equals("frame-received", StringComparison.InvariantCultureIgnoreCase)) { - case "frame-received": - { - return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; - } - case "pointer-released": - { - return new InputProtocol.PointerReleasedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }; - } - case "pointer-pressed": - { - return new InputProtocol.PointerPressedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }; - } - case "pointer-moved": - { - return new InputProtocol.PointerMovedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - }; - } - case "scroll": - { - return new InputProtocol.ScrollEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - DeltaX = ParseDouble(parts[4]), - DeltaY = ParseDouble(parts[5]), - }; - } - default: - return null; + return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; } + else if (key.Equals("pointer-released", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.PointerReleasedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; + } + else if (key.Equals("pointer-pressed", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.PointerPressedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; + } + else if (key.Equals("pointer-moved", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.PointerMovedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + }; + } + else if (key.Equals("scroll", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.ScrollEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + DeltaX = ParseDouble(parts[4]), + DeltaY = ParseDouble(parts[5]), + }; + } + + return null; } private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) => From 4df6d86e0321f712177937ed5f7d82224f91c685 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 1 Sep 2020 09:24:23 +0300 Subject: [PATCH 024/446] Revert "Fix comparing strings with InvariantCultureIgnoreCase" This reverts commit cf42bc3bd0899545149d947127a1fa0bef8c97d9. --- .../Remote/HtmlTransport/HtmlTransport.cs | 92 ++++++++++--------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 1d7fd91756..ded9ad6b11 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -264,53 +264,55 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport private static object ParseMessage(string message) { var parts = message.Split(':'); - var key = parts[0]; - if (key.Equals("frame-received", StringComparison.InvariantCultureIgnoreCase)) + switch (parts[0]) { - return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; - } - else if (key.Equals("pointer-released", StringComparison.InvariantCultureIgnoreCase)) - { - return new InputProtocol.PointerReleasedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }; - } - else if (key.Equals("pointer-pressed", StringComparison.InvariantCultureIgnoreCase)) - { - return new InputProtocol.PointerPressedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }; - } - else if (key.Equals("pointer-moved", StringComparison.InvariantCultureIgnoreCase)) - { - return new InputProtocol.PointerMovedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - }; - } - else if (key.Equals("scroll", StringComparison.InvariantCultureIgnoreCase)) - { - return new InputProtocol.ScrollEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - DeltaX = ParseDouble(parts[4]), - DeltaY = ParseDouble(parts[5]), - }; + case "frame-received": + { + return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; + } + case "pointer-released": + { + return new InputProtocol.PointerReleasedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; + } + case "pointer-pressed": + { + return new InputProtocol.PointerPressedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; + } + case "pointer-moved": + { + return new InputProtocol.PointerMovedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + }; + } + case "scroll": + { + return new InputProtocol.ScrollEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + DeltaX = ParseDouble(parts[4]), + DeltaY = ParseDouble(parts[5]), + }; + } + default: + return null; } - - return null; } private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) => From c019f3a3f9ca84850aa24de75d111da7d87acf5a Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 1 Sep 2020 09:27:30 +0300 Subject: [PATCH 025/446] Fix API key to lower invariant --- .../Remote/HtmlTransport/HtmlTransport.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index ded9ad6b11..920f155cfd 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -264,7 +264,8 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport private static object ParseMessage(string message) { var parts = message.Split(':'); - switch (parts[0]) + var key = parts[0].ToLowerInvariant(); + switch (key) { case "frame-received": { From fe1a847847df1179201709db1a11d35e09abc54a Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 1 Sep 2020 09:42:50 +0300 Subject: [PATCH 026/446] Revert "Fix API key to lower invariant" This reverts commit c019f3a3f9ca84850aa24de75d111da7d87acf5a. --- .../Remote/HtmlTransport/HtmlTransport.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 920f155cfd..ded9ad6b11 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -264,8 +264,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport private static object ParseMessage(string message) { var parts = message.Split(':'); - var key = parts[0].ToLowerInvariant(); - switch (key) + switch (parts[0]) { case "frame-received": { From 5f4f52f78d4075731f878526632a30ffbd664c68 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Tue, 1 Sep 2020 09:43:43 +0300 Subject: [PATCH 027/446] Fix comparing strings with InvariantCultureIgnoreCase --- .../Remote/HtmlTransport/HtmlTransport.cs | 92 +++++++++---------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index ded9ad6b11..1d7fd91756 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -264,55 +264,53 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport private static object ParseMessage(string message) { var parts = message.Split(':'); - switch (parts[0]) + var key = parts[0]; + if (key.Equals("frame-received", StringComparison.InvariantCultureIgnoreCase)) { - case "frame-received": - { - return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; - } - case "pointer-released": - { - return new InputProtocol.PointerReleasedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }; - } - case "pointer-pressed": - { - return new InputProtocol.PointerPressedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - Button = ParseMouseButton(parts[4]), - }; - } - case "pointer-moved": - { - return new InputProtocol.PointerMovedEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - }; - } - case "scroll": - { - return new InputProtocol.ScrollEventMessage - { - Modifiers = ParseInputModifiers(parts[1]), - X = ParseDouble(parts[2]), - Y = ParseDouble(parts[3]), - DeltaX = ParseDouble(parts[4]), - DeltaY = ParseDouble(parts[5]), - }; - } - default: - return null; + return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; } + else if (key.Equals("pointer-released", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.PointerReleasedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; + } + else if (key.Equals("pointer-pressed", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.PointerPressedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + Button = ParseMouseButton(parts[4]), + }; + } + else if (key.Equals("pointer-moved", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.PointerMovedEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + }; + } + else if (key.Equals("scroll", StringComparison.InvariantCultureIgnoreCase)) + { + return new InputProtocol.ScrollEventMessage + { + Modifiers = ParseInputModifiers(parts[1]), + X = ParseDouble(parts[2]), + Y = ParseDouble(parts[3]), + DeltaX = ParseDouble(parts[4]), + DeltaY = ParseDouble(parts[5]), + }; + } + + return null; } private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) => From bcf502e1ca158db751519a8c69819a8aa983030a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 03:41:52 -0700 Subject: [PATCH 028/446] texture offsets --- .../ControlCatalog.NetCore/Properties/launchSettings.json | 8 ++++++++ src/Avalonia.OpenGL/Angle/AngleEglInterface.cs | 8 ++++---- src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs | 4 ++-- src/Avalonia.OpenGL/EglConsts.cs | 4 ++++ src/Windows/Avalonia.Win32/Composition/CompositionHost.cs | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 samples/ControlCatalog.NetCore/Properties/launchSettings.json diff --git a/samples/ControlCatalog.NetCore/Properties/launchSettings.json b/samples/ControlCatalog.NetCore/Properties/launchSettings.json new file mode 100644 index 0000000000..cd8a2c824b --- /dev/null +++ b/samples/ControlCatalog.NetCore/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "ControlCatalog.NetCore": { + "commandName": "Project", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs index 375b93c27c..ef0ddf65b6 100644 --- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs +++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs @@ -7,8 +7,8 @@ namespace Avalonia.OpenGL.Angle { public class AngleEglInterface : EglInterface { - [DllImport("libegl.dll", CharSet = CharSet.Ansi)] - static extern IntPtr eglGetProcAddress(string proc); + [DllImport("libglesv2.dll", CharSet = CharSet.Ansi)] + static extern IntPtr EGL_GetProcAddress(string proc); public AngleEglInterface() : base(LoadAngle()) { @@ -20,10 +20,10 @@ namespace Avalonia.OpenGL.Angle if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) throw new PlatformNotSupportedException(); { - var disp = eglGetProcAddress("eglGetPlatformDisplayEXT"); + var disp = EGL_GetProcAddress("eglGetPlatformDisplayEXT"); if (disp == IntPtr.Zero) throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point"); - return eglGetProcAddress; + return EGL_GetProcAddress; } } diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs index 1a42ed90c2..af10211f15 100644 --- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs +++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs @@ -78,11 +78,11 @@ namespace Avalonia.OpenGL.Angle return d3dDeviceHandle; } - public EglSurface WrapDirect3D11Texture(IntPtr handle) + public EglSurface WrapDirect3D11Texture(IntPtr handle, int offsetX, int offsetY) { if (PlatformApi != AngleOptions.PlatformApi.DirectX11) throw new InvalidOperationException("Current platform API is " + PlatformApi); - return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE }); + return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE, EGL_NONE }); } } } diff --git a/src/Avalonia.OpenGL/EglConsts.cs b/src/Avalonia.OpenGL/EglConsts.cs index 8e44004f2d..c644df97bf 100644 --- a/src/Avalonia.OpenGL/EglConsts.cs +++ b/src/Avalonia.OpenGL/EglConsts.cs @@ -205,5 +205,9 @@ namespace Avalonia.OpenGL public const int EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE = 0x3200; public const int EGL_D3D_TEXTURE_ANGLE = 0x33A3; + + + public const int EGL_TEXTURE_OFFSET_X_ANGLE = 0x3490; + public const int EGL_TEXTURE_OFFSET_Y_ANGLE = 0x3491; } } diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 1735b22035..afd5ff4e39 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -94,7 +94,7 @@ namespace Avalonia.Win32 ref iid, out IntPtr texture, ref offset); - var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture); + var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y); return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); } From e6cf04d4e1eb6f2955cf495a65c125ceb391111d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 03:58:34 -0700 Subject: [PATCH 029/446] update egl properties for wrapped texture. --- src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs | 4 ++-- src/Avalonia.OpenGL/EglConsts.cs | 2 ++ src/Windows/Avalonia.Win32/Composition/CompositionHost.cs | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs index af10211f15..3b254d401a 100644 --- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs +++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs @@ -78,11 +78,11 @@ namespace Avalonia.OpenGL.Angle return d3dDeviceHandle; } - public EglSurface WrapDirect3D11Texture(IntPtr handle, int offsetX, int offsetY) + public EglSurface WrapDirect3D11Texture(IntPtr handle, int offsetX, int offsetY, int width, int height) { if (PlatformApi != AngleOptions.PlatformApi.DirectX11) throw new InvalidOperationException("Current platform API is " + PlatformApi); - return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE, EGL_NONE }); + return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE }); } } } diff --git a/src/Avalonia.OpenGL/EglConsts.cs b/src/Avalonia.OpenGL/EglConsts.cs index c644df97bf..5910e32ee1 100644 --- a/src/Avalonia.OpenGL/EglConsts.cs +++ b/src/Avalonia.OpenGL/EglConsts.cs @@ -209,5 +209,7 @@ namespace Avalonia.OpenGL public const int EGL_TEXTURE_OFFSET_X_ANGLE = 0x3490; public const int EGL_TEXTURE_OFFSET_Y_ANGLE = 0x3491; + + public const int EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE = 0x33A6; } } diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index afd5ff4e39..427655adf1 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -87,14 +87,14 @@ namespace Avalonia.Win32 if (lastSize.X != _info.Size.Width || lastSize.Y != _info.Size.Height) { lastSize = new POINT { X = _info.Size.Width, Y = _info.Size.Height }; - // _surfaceInterop.Resize(lastSize); + _surfaceInterop.Resize(lastSize); } _surfaceInterop.BeginDraw( ref updateRect, ref iid, out IntPtr texture, ref offset); - var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y); + var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); } From b6667530a25ae46916d11c4f76df83a3b0b7ba04 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 06:27:51 -0700 Subject: [PATCH 030/446] force ui thread rendering for now. --- src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs | 2 +- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs index 3b254d401a..c1f0f376b1 100644 --- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs +++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs @@ -82,7 +82,7 @@ namespace Avalonia.OpenGL.Angle { if (PlatformApi != AngleOptions.PlatformApi.DirectX11) throw new InvalidOperationException("Current platform API is " + PlatformApi); - return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE }); + return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE }); } } } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index af6058d197..8840dcacef 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -88,7 +88,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToConstant(s_instance) .Bind().ToConstant(new RenderLoop()) - .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new UiThreadRenderTimer(60)) .Bind().ToSingleton() .Bind().ToConstant(s_instance) .Bind().ToSingleton() From 3e96718d90e2c8bad28ab4110a4464e4e418ada9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 07:13:55 -0700 Subject: [PATCH 031/446] simplify hack to clear window. --- .../Composition/CompositionHost.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 427655adf1..b810ec45e0 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -64,18 +64,7 @@ namespace Avalonia.Win32 using (var target = windowSurface.CreateGlRenderTarget()) { - using (var session = target.BeginDraw()) - { - using (session.Context.MakeCurrent()) - { - var gl = _context.GlInterface; - gl.Viewport(0, 0, _info.Size.Width, _info.Size.Height); - gl.ClearStencil(0); - gl.ClearColor(0, 0, 0, 0); - gl.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_DEPTH_BUFFER_BIT); - gl.Flush(); - } - } + target.BeginDraw().Dispose(); } } @@ -83,12 +72,7 @@ namespace Avalonia.Win32 var iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); var updateRect = new RECT { right = _info.Size.Width, bottom = _info.Size.Height }; var offset = new POINT(); - - if (lastSize.X != _info.Size.Width || lastSize.Y != _info.Size.Height) - { - lastSize = new POINT { X = _info.Size.Width, Y = _info.Size.Height }; - _surfaceInterop.Resize(lastSize); - } + _surfaceInterop.BeginDraw( ref updateRect, ref iid, From 40f45115fbbe1e6a9dddc075ef5d926829e34768 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 07:14:28 -0700 Subject: [PATCH 032/446] remove unnecessary calls. --- src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs index 081db5d26a..ce4c6ecb90 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs @@ -72,10 +72,6 @@ namespace Avalonia.Skia $"Can't create drawing context for surface with {size} size and {scaling} scaling"); } - gl.Viewport(0, 0, size.Width, size.Height); - gl.ClearStencil(0); - gl.ClearColor(0, 0, 0, 0); - gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); lock (_grContext) { _grContext.ResetContext(); @@ -88,6 +84,7 @@ namespace Avalonia.Skia SKColorType.Rgba8888); success = true; + return new GlGpuSession(_grContext, renderTarget, surface, glSession); } } From 3deae8dbff8e4734e96b1f6aed2791f0eb989ee1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 07:32:53 -0700 Subject: [PATCH 033/446] no redirection bitmap --- .../Avalonia.Win32/Composition/CompositionHost.cs | 13 ------------- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index b810ec45e0..6d293a31eb 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -53,22 +53,9 @@ namespace Avalonia.Win32 public override bool IsCorrupted => _initialSize != _info.Size; - bool _firstRun = true; POINT lastSize; public override IGlPlatformSurfaceRenderingSession BeginDraw() { - if (_firstRun) - { - _firstRun = false; - var windowSurface = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, _info); - - using (var target = windowSurface.CreateGlRenderTarget()) - { - target.BeginDraw().Dispose(); - } - } - - var iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); var updateRect = new RECT { right = _info.Size.Width, bottom = _info.Size.Height }; var offset = new POINT(); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 25ae755990..3047f44a71 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -626,7 +626,7 @@ namespace Avalonia.Win32 protected virtual IntPtr CreateWindowOverride(ushort atom) { return CreateWindowEx( - 0, + (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP, // TODO this is only when using Win UI Comp. atom, null, (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN, From 2981cc470dc2258450c1b6dae80e405f1ab357a9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 08:46:12 -0700 Subject: [PATCH 034/446] use render thread. --- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 8840dcacef..af6058d197 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -88,7 +88,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToConstant(s_instance) .Bind().ToConstant(new RenderLoop()) - .Bind().ToConstant(new UiThreadRenderTimer(60)) + .Bind().ToConstant(new DefaultRenderTimer(60)) .Bind().ToSingleton() .Bind().ToConstant(s_instance) .Bind().ToSingleton() From 62e2a2d2e64426ed69c0b97cf41ffbb6b386b7e6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 16:59:14 +0100 Subject: [PATCH 035/446] remove override directx api. --- samples/ControlCatalog.NetCore/Program.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 9fcf618614..142736a0bb 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -10,8 +10,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Headless; using Avalonia.LogicalTree; using Avalonia.Skia; -using Avalonia.Dialogs; -using Avalonia.OpenGL; using Avalonia.ReactiveUI; using Avalonia.Threading; using Avalonia.Dialogs; @@ -120,10 +118,6 @@ namespace ControlCatalog.NetCore EnableMultitouch = true, AllowEglInitialization = true }) - .With(new AngleOptions - { - AllowedPlatformApis = new System.Collections.Generic.List { AngleOptions.PlatformApi.DirectX11} - }) .UseSkia() .UseReactiveUI() .UseManagedSystemDialogs() From 758ddafad888f0791a6a0cd256b11ca97ac24971 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 17:02:38 +0100 Subject: [PATCH 036/446] restore file. --- src/Avalonia.OpenGL/Angle/AngleEglInterface.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs index ef0ddf65b6..375b93c27c 100644 --- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs +++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs @@ -7,8 +7,8 @@ namespace Avalonia.OpenGL.Angle { public class AngleEglInterface : EglInterface { - [DllImport("libglesv2.dll", CharSet = CharSet.Ansi)] - static extern IntPtr EGL_GetProcAddress(string proc); + [DllImport("libegl.dll", CharSet = CharSet.Ansi)] + static extern IntPtr eglGetProcAddress(string proc); public AngleEglInterface() : base(LoadAngle()) { @@ -20,10 +20,10 @@ namespace Avalonia.OpenGL.Angle if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) throw new PlatformNotSupportedException(); { - var disp = EGL_GetProcAddress("eglGetPlatformDisplayEXT"); + var disp = eglGetProcAddress("eglGetPlatformDisplayEXT"); if (disp == IntPtr.Zero) throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point"); - return EGL_GetProcAddress; + return eglGetProcAddress; } } From e68ce5643e815210d0b8f8b622eed3aae30e74dd Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 09:05:24 -0700 Subject: [PATCH 037/446] Revert "restore file." This reverts commit 758ddafad888f0791a6a0cd256b11ca97ac24971. --- src/Avalonia.OpenGL/Angle/AngleEglInterface.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs index 375b93c27c..ef0ddf65b6 100644 --- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs +++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs @@ -7,8 +7,8 @@ namespace Avalonia.OpenGL.Angle { public class AngleEglInterface : EglInterface { - [DllImport("libegl.dll", CharSet = CharSet.Ansi)] - static extern IntPtr eglGetProcAddress(string proc); + [DllImport("libglesv2.dll", CharSet = CharSet.Ansi)] + static extern IntPtr EGL_GetProcAddress(string proc); public AngleEglInterface() : base(LoadAngle()) { @@ -20,10 +20,10 @@ namespace Avalonia.OpenGL.Angle if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) throw new PlatformNotSupportedException(); { - var disp = eglGetProcAddress("eglGetPlatformDisplayEXT"); + var disp = EGL_GetProcAddress("eglGetPlatformDisplayEXT"); if (disp == IntPtr.Zero) throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point"); - return eglGetProcAddress; + return EGL_GetProcAddress; } } From f068fa02e989e55ac3d24c944a0aa66837480dad Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Sep 2020 09:14:37 -0700 Subject: [PATCH 038/446] restore render timer. --- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index af6058d197..8840dcacef 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -88,7 +88,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToConstant(s_instance) .Bind().ToConstant(new RenderLoop()) - .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new UiThreadRenderTimer(60)) .Bind().ToSingleton() .Bind().ToConstant(s_instance) .Bind().ToSingleton() From 1025b1240247793e19415e797100882952dc4043 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Sat, 12 Sep 2020 00:27:05 +0500 Subject: [PATCH 039/446] Add tests --- .editorconfig | 3 + .../src/Models/Input/InputEventMessageBase.ts | 4 +- .../src/Models/Input/MouseEventHelpers.ts | 18 +- .../Models/Input/PointerEventMessageBase.ts | 2 +- .../Models/Input/PointerMovedEventMessage.ts | 2 +- .../Input/PointerPressedEventMessage.ts | 6 +- .../Input/PointerReleasedEventMessage.ts | 6 +- .../src/Models/Input/ScrollEventMessage.ts | 2 +- .../Remote/HtmlTransport/webapp/.gitignore | 2 + .../webapp/Models/InputEventTests.ts | 73 + .../HtmlTransport/webapp/package-lock.json | 2414 +++++++++++++++++ .../Remote/HtmlTransport/webapp/package.json | 26 + .../Remote/HtmlTransport/webapp/tsconfig.json | 12 + 13 files changed, 2551 insertions(+), 19 deletions(-) create mode 100644 tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore create mode 100644 tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts create mode 100644 tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json create mode 100644 tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json create mode 100644 tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/tsconfig.json diff --git a/.editorconfig b/.editorconfig index f6bce9cb76..c7a381b730 100644 --- a/.editorconfig +++ b/.editorconfig @@ -156,6 +156,9 @@ indent_size = 2 [*.{props,targets,config,nuspec}] indent_size = 2 +[*.json] +indent_size = 2 + # Shell scripts [*.sh] end_of_line = lf diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts index 2da7424e2a..bfe5aad093 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts @@ -1,6 +1,6 @@ import * as React from "react"; -import {InputModifiers} from "src/Models/Input/InputModifiers"; -import {getModifiers} from "src/Models/Input/MouseEventHelpers"; +import {InputModifiers} from "./InputModifiers"; +import {getModifiers} from "./MouseEventHelpers"; export abstract class InputEventMessageBase { public readonly modifiers : Array; diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts index 3f85959d6f..f2cd1f4d46 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts @@ -1,6 +1,6 @@ import * as React from "react"; -import {InputModifiers} from "src/Models/Input/InputModifiers"; -import {MouseButton} from "src/Models/Input/MouseButton"; +import {InputModifiers} from "./InputModifiers"; +import {MouseButton} from "./MouseButton"; export function getModifiers(e: React.MouseEvent): Array { @@ -14,12 +14,14 @@ export function getModifiers(e: React.MouseEvent): Array { modifiers.push(InputModifiers.Shift); if (e.metaKey) modifiers.push(InputModifiers.Windows); - if ((e.buttons & 1) != 0) - modifiers.push(InputModifiers.LeftMouseButton); - if ((e.buttons & 2) != 0) - modifiers.push(InputModifiers.RightMouseButton); - if ((e.buttons & 4) != 0) - modifiers.push(InputModifiers.MiddleMouseButton); + if (e.buttons != 0) { + if ((e.buttons & 1) != 0) + modifiers.push(InputModifiers.LeftMouseButton); + if ((e.buttons & 2) != 0) + modifiers.push(InputModifiers.RightMouseButton); + if ((e.buttons & 4) != 0) + modifiers.push(InputModifiers.MiddleMouseButton); + } return modifiers; } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts index fc304c2965..c9de132ff4 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts @@ -1,5 +1,5 @@ import * as React from "react"; -import {InputEventMessageBase} from "src/Models/Input/InputEventMessageBase"; +import {InputEventMessageBase} from "./InputEventMessageBase"; export abstract class PointerEventMessageBase extends InputEventMessageBase { public readonly x: number; diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts index cd135dfa39..3f782d1d46 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts @@ -1,5 +1,5 @@ import * as React from "react"; -import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; +import {PointerEventMessageBase} from "./PointerEventMessageBase"; export class PointerMovedEventMessage extends PointerEventMessageBase { constructor(e: React.MouseEvent) { diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts index e75551e0b3..0ae06cc064 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts @@ -1,7 +1,7 @@ import * as React from "react"; -import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; -import {MouseButton} from "src/Models/Input/MouseButton"; -import {getMouseButton} from "src/Models/Input/MouseEventHelpers"; +import {PointerEventMessageBase} from "./PointerEventMessageBase"; +import {MouseButton} from "./MouseButton"; +import {getMouseButton} from "./MouseEventHelpers"; export class PointerPressedEventMessage extends PointerEventMessageBase { public readonly button: MouseButton diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts index c2bc368323..fb1477b554 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts @@ -1,7 +1,7 @@ import * as React from "react"; -import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; -import {MouseButton} from "src/Models/Input/MouseButton"; -import {getMouseButton} from "src/Models/Input/MouseEventHelpers"; +import {PointerEventMessageBase} from "./PointerEventMessageBase"; +import {MouseButton} from "./MouseButton"; +import {getMouseButton} from "./MouseEventHelpers"; export class PointerReleasedEventMessage extends PointerEventMessageBase { public readonly button: MouseButton diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts index 427ccc5e5e..b11be19d82 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts @@ -1,5 +1,5 @@ import * as React from "react"; -import {PointerEventMessageBase} from "src/Models/Input/PointerEventMessageBase"; +import {PointerEventMessageBase} from "./PointerEventMessageBase"; export class ScrollEventMessage extends PointerEventMessageBase { public readonly deltaX: number; diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore new file mode 100644 index 0000000000..e3fbd98336 --- /dev/null +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore @@ -0,0 +1,2 @@ +build +node_modules diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts new file mode 100644 index 0000000000..061bb74be7 --- /dev/null +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts @@ -0,0 +1,73 @@ +import {describe} from 'mocha'; +import {expect} from 'chai'; +import {Mock} from "moq.ts"; +import {MouseEvent, WheelEvent} from "react"; +import {InputModifiers} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers"; +import {MouseButton} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton"; +import {PointerMovedEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage"; +import {PointerPressedEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage"; +import {PointerReleasedEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage"; +import {ScrollEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage"; + +const x = .3 +const y = .42 +const modifiers = [ + InputModifiers.Alt, + InputModifiers.Control, + InputModifiers.Shift, + InputModifiers.Windows, + InputModifiers.LeftMouseButton, + InputModifiers.RightMouseButton, + InputModifiers.MiddleMouseButton +] + +const button = MouseButton.Left + +const deltaX = -3. +const deltaY = -3. + +const mouseEvent = new Mock() + .setup(x => x.altKey).returns(true) + .setup(x => x.ctrlKey).returns(true) + .setup(x => x.shiftKey).returns(true) + .setup(x => x.metaKey).returns(true) + .setup(x => x.buttons).returns(7) + .setup(x => x.button).returns(0) + .setup(x => x.clientX).returns(x) + .setup(x => x.clientY).returns(y) + .object() + +const wheelEvent = new Mock() + .setup(x => x.altKey).returns(true) + .setup(x => x.ctrlKey).returns(true) + .setup(x => x.shiftKey).returns(true) + .setup(x => x.metaKey).returns(true) + .setup(x => x.buttons).returns(7) + .setup(x => x.clientX).returns(x) + .setup(x => x.clientY).returns(y) + .setup(x => x.deltaX).returns(-deltaX) + .setup(x => x.deltaY).returns(-deltaY) + .object() + +describe("Input event tests", () => { + it("PointerMovedEventMessage", () => { + const message = new PointerMovedEventMessage(mouseEvent) + expect(message.toString()) + .equal(`pointer-moved:${modifiers}:${x}:${y}`) + }) + it("PointerPressedEventMessage", () => { + const message = new PointerPressedEventMessage(mouseEvent) + expect(message.toString()) + .equal(`pointer-pressed:${modifiers}:${x}:${y}:${button}`) + }) + it("PointerReleasedEventMessage", () => { + const message = new PointerReleasedEventMessage(mouseEvent) + expect(message.toString()) + .equal(`pointer-released:${modifiers}:${x}:${y}:${button}`) + }) + it("ScrollEventMessage", () => { + const message = new ScrollEventMessage(wheelEvent) + expect(message.toString()) + .equal(`scroll:${modifiers}:${x}:${y}:${deltaX}:${deltaY}`) + }) +}) diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json new file mode 100644 index 0000000000..eb707cb73f --- /dev/null +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json @@ -0,0 +1,2414 @@ +{ + "name": "simple-test", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", + "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.6", + "@babel/helper-module-transforms": "^7.11.0", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.11.5", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.11.5", + "@babel/types": "^7.11.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", + "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", + "dev": true, + "requires": { + "@babel/types": "^7.11.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", + "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", + "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/types": "^7.11.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "dev": true + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", + "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@types/chai": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz", + "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==", + "dev": true + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/mocha": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz", + "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.9.49", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz", + "integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "fromentries": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.1.tgz", + "integrity": "sha512-Xu2Qh8yqYuDhQGOhD5iJGninErSfI9A3FrriD3tjUgV5VbJFeH8vfgZ9HnC6jWN80QDVNQK5vmxRAmEAp7Mevw==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "hasha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", + "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + }, + "is-callable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", + "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mocha": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", + "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.2", + "debug": "4.1.1", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "object.assign": "4.1.0", + "promise.allsettled": "1.0.2", + "serialize-javascript": "4.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.0", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.1" + } + }, + "moq.ts": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/moq.ts/-/moq.ts-6.4.0.tgz", + "integrity": "sha512-g9siyKOvYGVhfDCl9mpLptch93egRC3yXIbPLssxukOROl368XBB+6M7YjLTkjVRHbKbpJBD6P7od+Ky/0xWIw==", + "dev": true, + "requires": { + "tslib": "^2.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", + "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", + "dev": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", + "flat": "^4.1.0", + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json new file mode 100644 index 0000000000..dbb12e192a --- /dev/null +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json @@ -0,0 +1,26 @@ +{ + "name": "simple-test", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "mocha -r ts-node/register ./**/*Tests.ts", + "coverage": "nyc -r text -e .ts -x \"./*Tests.ts\" npm run test" + }, + "type": "module", + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": {}, + "devDependencies": { + "@types/chai": "4.2.12", + "@types/mocha": "8.0.3", + "@types/react": "^16.3.14", + "chai": "^4.2.0", + "mocha": "^8.1.3", + "moq.ts": "^6.4.0", + "nyc": "^15.1.0", + "react": "^16.3.2", + "ts-node": "^9.0.0", + "typescript": "^4.0.2" + } +} diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/tsconfig.json b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/tsconfig.json new file mode 100644 index 0000000000..fdb82879fc --- /dev/null +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "sourceMap": true, + "strict": true, + "esModuleInterop": true + }, + "exclude": [ + "node_modules" + ] +} From a52464ebb9b31c1e03e89b0c39705f5c942e61d7 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Sat, 12 Sep 2020 20:19:58 +0500 Subject: [PATCH 040/446] Add html previewer tests to nuke --- build.ps1 | 4 ++-- nukebuild/Build.cs | 16 ++++++++++++++++ nukebuild/BuildParameters.cs | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/build.ps1 b/build.ps1 index 57e2f80075..98a9c9afee 100644 --- a/build.ps1 +++ b/build.ps1 @@ -43,7 +43,7 @@ if (Test-Path $DotNetGlobalFile) { } # If dotnet is installed locally, and expected version is not set or installation matches the expected version -if ((Get-Command "dotnet" -ErrorAction SilentlyContinue) -ne $null -and ` +if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) { $env:DOTNET_EXE = (Get-Command "dotnet").Path } @@ -53,7 +53,7 @@ else { # Download install script $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" - md -force $TempDirectory > $null + mkdir -force $TempDirectory > $null (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) # Install by channel or version diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 24398accbb..c56703f9cc 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -223,6 +223,21 @@ partial class Build : NukeBuild } } + Target RunHtmlPreviewerTests => _ => _ + .DependsOn(CompileHtmlPreviewer) + .OnlyWhenStatic(() => !(Parameters.SkipPreviewer || Parameters.SkipTests)) + .Executes(() => + { + var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp"; + + NpmTasks.NpmInstall(c => c + .SetWorkingDirectory(webappTestDir) + .SetArgumentConfigurator(a => a.Add("--silent"))); + NpmTasks.NpmRun(c => c + .SetWorkingDirectory(webappTestDir) + .SetCommand("test")); + }); + Target RunCoreLibsTests => _ => _ .OnlyWhenStatic(() => !Parameters.SkipTests) .DependsOn(Compile) @@ -323,6 +338,7 @@ partial class Build : NukeBuild .DependsOn(RunCoreLibsTests) .DependsOn(RunRenderTests) .DependsOn(RunDesignerTests) + .DependsOn(RunHtmlPreviewerTests) .DependsOn(RunLeakTests); Target Package => _ => _ diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index a167e9d892..c76019d9eb 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -62,7 +62,7 @@ public partial class Build public AbsolutePath ZipTargetControlCatalogDesktopDir { get; } - public BuildParameters(Build b) + public BuildParameters(Build b) { // ARGUMENTS Configuration = b.Configuration ?? "Release"; From 8fd50dd15aa569b2f41583a64c690d0233d3d237 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Sat, 12 Sep 2020 20:49:27 +0500 Subject: [PATCH 041/446] Add some tests --- .../Remote/HtmlTransport/webapp/.gitignore | 1 + .../webapp/Models/InputEventTests.ts | 151 ++++++++++-------- 2 files changed, 87 insertions(+), 65 deletions(-) diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore index e3fbd98336..dc33b94747 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore @@ -1,2 +1,3 @@ build node_modules +.nyc_output \ No newline at end of file diff --git a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts index 061bb74be7..e96c4d4731 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts +++ b/tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts @@ -1,73 +1,94 @@ -import {describe} from 'mocha'; -import {expect} from 'chai'; -import {Mock} from "moq.ts"; -import {MouseEvent, WheelEvent} from "react"; -import {InputModifiers} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers"; -import {MouseButton} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton"; -import {PointerMovedEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage"; -import {PointerPressedEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage"; -import {PointerReleasedEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage"; -import {ScrollEventMessage} from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage"; +import { describe } from 'mocha'; +import { expect } from 'chai'; +import { Mock } from "moq.ts"; +import { MouseEvent, WheelEvent } from "react"; +import { InputModifiers } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers"; +import { MouseButton } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton"; +import { PointerMovedEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage"; +import { PointerPressedEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage"; +import { PointerReleasedEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage"; +import { ScrollEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage"; +import { getModifiers, getMouseButton } from '../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers'; -const x = .3 -const y = .42 -const modifiers = [ - InputModifiers.Alt, - InputModifiers.Control, - InputModifiers.Shift, - InputModifiers.Windows, - InputModifiers.LeftMouseButton, - InputModifiers.RightMouseButton, - InputModifiers.MiddleMouseButton -] +describe("Input event tests", () => { + describe("Helpers", () => { + it("getModifiers", () => { + const event = new Mock() + .setup(x => x.altKey).returns(false) + .setup(x => x.ctrlKey).returns(true) + .setup(x => x.shiftKey).returns(false) + .setup(x => x.metaKey).returns(false) + .setup(x => x.buttons).returns(1) + .object() + var actual = getModifiers(event) -const button = MouseButton.Left + expect(actual) + .eql([InputModifiers.Control, InputModifiers.LeftMouseButton]) + }) + it("getMouseButton", () => { + const event = new Mock() + .setup(x => x.button).returns(1) + .object() + var actual = getMouseButton(event) -const deltaX = -3. -const deltaY = -3. + expect(actual) + .equal(MouseButton.Middle) + }) + }) -const mouseEvent = new Mock() - .setup(x => x.altKey).returns(true) - .setup(x => x.ctrlKey).returns(true) - .setup(x => x.shiftKey).returns(true) - .setup(x => x.metaKey).returns(true) - .setup(x => x.buttons).returns(7) - .setup(x => x.button).returns(0) - .setup(x => x.clientX).returns(x) - .setup(x => x.clientY).returns(y) - .object() + describe("Messages", () => { + const x = .3 + const y = .42 + const modifiers = "0,1,2,3,4,5,6" -const wheelEvent = new Mock() - .setup(x => x.altKey).returns(true) - .setup(x => x.ctrlKey).returns(true) - .setup(x => x.shiftKey).returns(true) - .setup(x => x.metaKey).returns(true) - .setup(x => x.buttons).returns(7) - .setup(x => x.clientX).returns(x) - .setup(x => x.clientY).returns(y) - .setup(x => x.deltaX).returns(-deltaX) - .setup(x => x.deltaY).returns(-deltaY) - .object() + const button = "1" -describe("Input event tests", () => { - it("PointerMovedEventMessage", () => { - const message = new PointerMovedEventMessage(mouseEvent) - expect(message.toString()) - .equal(`pointer-moved:${modifiers}:${x}:${y}`) - }) - it("PointerPressedEventMessage", () => { - const message = new PointerPressedEventMessage(mouseEvent) - expect(message.toString()) - .equal(`pointer-pressed:${modifiers}:${x}:${y}:${button}`) - }) - it("PointerReleasedEventMessage", () => { - const message = new PointerReleasedEventMessage(mouseEvent) - expect(message.toString()) - .equal(`pointer-released:${modifiers}:${x}:${y}:${button}`) - }) - it("ScrollEventMessage", () => { - const message = new ScrollEventMessage(wheelEvent) - expect(message.toString()) - .equal(`scroll:${modifiers}:${x}:${y}:${deltaX}:${deltaY}`) + const deltaX = -3. + const deltaY = -3. + + const mouseEvent = new Mock() + .setup(x => x.altKey).returns(true) + .setup(x => x.ctrlKey).returns(true) + .setup(x => x.shiftKey).returns(true) + .setup(x => x.metaKey).returns(true) + .setup(x => x.buttons).returns(7) + .setup(x => x.button).returns(0) + .setup(x => x.clientX).returns(x) + .setup(x => x.clientY).returns(y) + .object() + + it("PointerMovedEventMessage", () => { + const message = new PointerMovedEventMessage(mouseEvent) + expect(message.toString()) + .equal(`pointer-moved:${modifiers}:${x}:${y}`) + }) + it("PointerPressedEventMessage", () => { + const message = new PointerPressedEventMessage(mouseEvent) + expect(message.toString()) + .equal(`pointer-pressed:${modifiers}:${x}:${y}:${button}`) + }) + it("PointerReleasedEventMessage", () => { + const message = new PointerReleasedEventMessage(mouseEvent) + expect(message.toString()) + .equal(`pointer-released:${modifiers}:${x}:${y}:${button}`) + }) + + it("ScrollEventMessage", () => { + const wheelEvent = new Mock() + .setup(x => x.altKey).returns(true) + .setup(x => x.ctrlKey).returns(true) + .setup(x => x.shiftKey).returns(true) + .setup(x => x.metaKey).returns(true) + .setup(x => x.buttons).returns(7) + .setup(x => x.clientX).returns(x) + .setup(x => x.clientY).returns(y) + .setup(x => x.deltaX).returns(-deltaX) + .setup(x => x.deltaY).returns(-deltaY) + .object() + const message = new ScrollEventMessage(wheelEvent) + + expect(message.toString()) + .equal(`scroll:${modifiers}:${x}:${y}:${deltaX}:${deltaY}`) + }) }) }) From 0de7f4d7e0c5141060038d4a7f1f55ab36c10e51 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 12 Sep 2020 11:24:21 -0700 Subject: [PATCH 042/446] pass egl_width / height. --- src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs index c1f0f376b1..3b254d401a 100644 --- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs +++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs @@ -82,7 +82,7 @@ namespace Avalonia.OpenGL.Angle { if (PlatformApi != AngleOptions.PlatformApi.DirectX11) throw new InvalidOperationException("Current platform API is " + PlatformApi); - return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE }); + return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE }); } } } From 75025a15c195cf91197674f78a368e9ee6ac3da0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 12 Sep 2020 11:46:32 -0700 Subject: [PATCH 043/446] avalonia never resizes so use intptr for update rect. --- .../Avalonia.Win32/Composition/CompositionHost.cs | 7 +++---- .../Composition/ICompositionDrawingSurfaceInterop.cs | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 6d293a31eb..5d13109dda 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -39,6 +39,7 @@ namespace Avalonia.Win32 private readonly IEglWindowGlPlatformSurfaceInfo _info; private PixelSize _initialSize; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; + private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); public CompositionRenderTarget(EglDisplay display, EglContext context, ICompositionDrawingSurfaceInterop interopSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) { @@ -56,13 +57,11 @@ namespace Avalonia.Win32 POINT lastSize; public override IGlPlatformSurfaceRenderingSession BeginDraw() { - var iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); - var updateRect = new RECT { right = _info.Size.Width, bottom = _info.Size.Height }; var offset = new POINT(); _surfaceInterop.BeginDraw( - ref updateRect, - ref iid, + IntPtr.Zero, + ref s_Iid, out IntPtr texture, ref offset); var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs index 8fea5f293b..2eac796376 100644 --- a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs +++ b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs @@ -25,7 +25,7 @@ namespace Windows.UI.Composition.Interop [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] public interface ICompositionDrawingSurfaceInterop { - void BeginDraw(ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point); + void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point); void EndDraw(); @@ -51,9 +51,9 @@ namespace ABI.Windows.UI.Composition.Interop { [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] - public struct Vftbl + public unsafe struct Vftbl { - public delegate int _BeginDraw(IntPtr ThisPtr, ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset); + public delegate int _BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset); public delegate int _EndDraw(IntPtr ThisPtr); public delegate int _Resize(IntPtr ThisPtr, POINT sizePixels); public delegate int _ResumeDraw(IntPtr ThisPtr); @@ -87,7 +87,7 @@ namespace ABI.Windows.UI.Composition.Interop Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); } - private static int Do_Abi_BeginDraw(IntPtr ThisPtr, ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset) + private static int Do_Abi_BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset) { updateObject = IntPtr.Zero; return 0; @@ -118,9 +118,9 @@ namespace ABI.Windows.UI.Composition.Interop _obj = obj; } - public void BeginDraw(ref RECT updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point) + public void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, ref updateRect, ref iid, out updateObject, ref point)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, updateRect, ref iid, out updateObject, ref point)); } public void EndDraw() From 688ba15b6a3c6b1010bb558cf8b224de589936b7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 01:53:54 -0700 Subject: [PATCH 044/446] seperate classes. --- .../CompositionEglGlPlatformSurface.cs | 75 +++++++++++++++++ .../Composition/CompositionHost.cs | 83 ++----------------- 2 files changed, 84 insertions(+), 74 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs new file mode 100644 index 0000000000..c5e99a4b5d --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Angle; +using Windows.UI.Composition.Interop; + +namespace Avalonia.Win32 +{ + public class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase + { + private readonly EglDisplay _display; + private readonly EglContext _context; + private readonly IEglWindowGlPlatformSurfaceInfo _info; + private ICompositionDrawingSurfaceInterop _surfaceInterop; + private Windows.UI.Composition.Visual _surface; + + public CompositionEglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base() + { + _display = context.Display; + _context = context; + _info = info; + } + + public void AttachToCompositionTree(IntPtr hwnd) + { + _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd, out _surface); + } + + public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() + { + return new CompositionRenderTarget(_display, _context, _surface, _surfaceInterop, _info); + } + + class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase + { + private readonly EglDisplay _display; + private readonly IEglWindowGlPlatformSurfaceInfo _info; + private PixelSize _initialSize; + private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; + private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); + private Windows.UI.Composition.Visual _compositionVisual; + + public CompositionRenderTarget(EglDisplay display, + EglContext context, + Windows.UI.Composition.Visual compositionVisual, + ICompositionDrawingSurfaceInterop interopSurface, + IEglWindowGlPlatformSurfaceInfo info) + : base(display, context) + { + _display = display; + _surfaceInterop = interopSurface; + _info = info; + _initialSize = info.Size; + _compositionVisual = compositionVisual; + _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + } + + public override bool IsCorrupted => _initialSize != _info.Size; + public override IGlPlatformSurfaceRenderingSession BeginDraw() + { + var offset = new POINT(); + + _surfaceInterop.BeginDraw( + IntPtr.Zero, + ref s_Iid, + out IntPtr texture, ref offset); + + var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); + + return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); }, true); + } + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 5d13109dda..48656e1816 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.InteropServices; -using Avalonia.OpenGL; using Avalonia.OpenGL.Angle; using Windows.UI.Composition; using Windows.UI.Composition.Interop; @@ -8,71 +7,6 @@ using WinRT; namespace Avalonia.Win32 { - public class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase - { - private readonly EglDisplay _display; - private readonly EglContext _context; - private readonly IEglWindowGlPlatformSurfaceInfo _info; - private ICompositionDrawingSurfaceInterop _surfaceInterop; - - public CompositionEglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base() - { - _display = context.Display; - _context = context; - _info = info; - } - - public void AttachToCompositionTree(IntPtr hwnd) - { - _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd); - } - - public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() - { - return new CompositionRenderTarget(_display, _context, _surfaceInterop, _info); - } - - class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase - { - private readonly EglDisplay _display; - private readonly EglContext _context; - private readonly IEglWindowGlPlatformSurfaceInfo _info; - private PixelSize _initialSize; - private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; - private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); - - public CompositionRenderTarget(EglDisplay display, EglContext context, ICompositionDrawingSurfaceInterop interopSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context) - { - _display = display; - _context = context; - _surfaceInterop = interopSurface; - _info = info; - _initialSize = info.Size; - lastSize = new POINT { X = _info.Size.Width, Y = _info.Size.Height }; - _surfaceInterop.Resize(lastSize); - } - - public override bool IsCorrupted => _initialSize != _info.Size; - - POINT lastSize; - public override IGlPlatformSurfaceRenderingSession BeginDraw() - { - var offset = new POINT(); - - _surfaceInterop.BeginDraw( - IntPtr.Zero, - ref s_Iid, - out IntPtr texture, ref offset); - - var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); - - return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); - } - } - } - - - class CompositionHost { internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE @@ -155,7 +89,7 @@ namespace Avalonia.Win32 _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); } - public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd) + public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual) { var target = CreateDesktopWindowTarget(hwnd); @@ -163,22 +97,23 @@ namespace Avalonia.Win32 Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - var surfaceInterop = surface.As(); + var surfaceInterop = surface.As(); var brush = _compositor.CreateSurfaceBrush(surface); - var visual = _compositor.CreateSpriteVisual(); - - visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); + visual.Brush = brush; - //_target.Root = visual; - + visual.Offset = new System.Numerics.Vector3(0, 0, 0); target.Root = CreateBlur(); var visuals = target.Root.As().Children; visuals.InsertAtTop(visual); + visual.CompositeMode = CompositionCompositeMode.SourceOver; + + surfaceVisual = visual; + return surfaceInterop; } @@ -204,7 +139,7 @@ namespace Avalonia.Win32 { var interop = _compositor.As(); - interop.CreateDesktopWindowTarget(window, false, out var windowTarget); + interop.CreateDesktopWindowTarget(window, true, out var windowTarget); return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); } From b0bc3f4ea8fd47237f946ad72e59da4d399a1568 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 01:56:40 -0700 Subject: [PATCH 045/446] check for windows 10 to decide to use windows.ui.comp tree to render into. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3047f44a71..d964e490c0 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -104,17 +104,17 @@ namespace Avalonia.Win32 _framebuffer = new FramebufferManager(_hwnd); if (Win32GlManager.EglFeature != null) - { - _gl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); - } - - if (_gl is CompositionEglGlPlatformSurface cgl) - { - cgl.AttachToCompositionTree(_hwnd); - } - else { - CompositionHost.Instance.InitialiseWindowCompositionTree(_hwnd); + if (Win32Platform.WindowsVersion.Major >= 10) + { + var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); + cgl.AttachToCompositionTree(_hwnd); + _gl = cgl; + } + else + { + _gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); + } } Screen = new ScreenImpl(); From 8913cea89ec85f4b4a24ea82c32ac70cbf603198 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 01:57:00 -0700 Subject: [PATCH 046/446] code tidy. --- .../CompositionEglGlPlatformSurface.cs | 15 +++---- .../Composition/CompositionHost.cs | 42 ++++--------------- 2 files changed, 15 insertions(+), 42 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index c5e99a4b5d..701070a6b5 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -27,7 +27,7 @@ namespace Avalonia.Win32 } public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() - { + { return new CompositionRenderTarget(_display, _context, _surface, _surfaceInterop, _info); } @@ -40,11 +40,11 @@ namespace Avalonia.Win32 private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); private Windows.UI.Composition.Visual _compositionVisual; - public CompositionRenderTarget(EglDisplay display, - EglContext context, - Windows.UI.Composition.Visual compositionVisual, - ICompositionDrawingSurfaceInterop interopSurface, - IEglWindowGlPlatformSurfaceInfo info) + public CompositionRenderTarget(EglDisplay display, + EglContext context, + Windows.UI.Composition.Visual compositionVisual, + ICompositionDrawingSurfaceInterop interopSurface, + IEglWindowGlPlatformSurfaceInfo info) : base(display, context) { _display = display; @@ -53,6 +53,7 @@ namespace Avalonia.Win32 _initialSize = info.Size; _compositionVisual = compositionVisual; _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); } public override bool IsCorrupted => _initialSize != _info.Size; @@ -67,7 +68,7 @@ namespace Avalonia.Win32 var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); - return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); }, true); + return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); } } } diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 48656e1816..8da205fdc6 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -48,41 +48,13 @@ namespace Avalonia.Win32 Initialize(); } - public void AddElement(CompositionTarget target, float size, float x, float y) - { - if (target.Root != null) - { - var visuals = target.Root.As().Children; - - var visual = _compositor.CreateSpriteVisual(); - - var element = _compositor.CreateSpriteVisual(); - var rand = new Random(); - - element.Brush = _compositor.CreateColorBrush(new Windows.UI.Color { A = 255, R = (byte)(rand.NextDouble() * 255), G = (byte)(rand.NextDouble() * 255), B = (byte)(rand.NextDouble() * 255) }); - element.Size = new System.Numerics.Vector2(size, size); - element.Offset = new System.Numerics.Vector3(x, y, 0.0f); - - var animation = _compositor.CreateVector3KeyFrameAnimation(); - var bottom = (float)600 - element.Size.Y; - animation.InsertKeyFrame(1, new System.Numerics.Vector3(element.Offset.X, bottom, 0)); - - animation.Duration = TimeSpan.FromSeconds(2); - animation.DelayTime = TimeSpan.FromSeconds(3); - element.StartAnimation("Offset", animation); - visuals.InsertAtTop(element); - - visuals.InsertAtTop(visual); - } - } - private void Initialize() { EnsureDispatcherQueue(); if (_dispatcherQueueController != null) - _compositor = new Windows.UI.Composition.Compositor(); + _compositor = new Compositor(); - var interop = _compositor.As(); + var interop = _compositor.As(); var display = Win32GlManager.EglFeature.Display as AngleWin32EglDisplay; @@ -91,19 +63,19 @@ namespace Avalonia.Win32 public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual) { - var target = CreateDesktopWindowTarget(hwnd); + var target = CreateDesktopWindowTarget(hwnd); var surface = _graphicsDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - var surfaceInterop = surface.As(); + var surfaceInterop = surface.As(); var brush = _compositor.CreateSurfaceBrush(surface); var visual = _compositor.CreateSpriteVisual(); - + visual.Brush = brush; - visual.Offset = new System.Numerics.Vector3(0, 0, 0); + visual.Offset = new System.Numerics.Vector3(0, 0, 0); target.Root = CreateBlur(); var visuals = target.Root.As().Children; @@ -139,7 +111,7 @@ namespace Avalonia.Win32 { var interop = _compositor.As(); - interop.CreateDesktopWindowTarget(window, true, out var windowTarget); + interop.CreateDesktopWindowTarget(window, false, out var windowTarget); return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); } From 0140d201a1d3ea1444f9397c35cba495e2a39283 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:08:43 -0700 Subject: [PATCH 047/446] use render thread. --- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 8840dcacef..af6058d197 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -88,7 +88,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToConstant(s_instance) .Bind().ToConstant(new RenderLoop()) - .Bind().ToConstant(new UiThreadRenderTimer(60)) + .Bind().ToConstant(new DefaultRenderTimer(60)) .Bind().ToSingleton() .Bind().ToConstant(s_instance) .Bind().ToSingleton() From 75bf2fcb67e7f63b53aec383c89ad5e18bb0c742 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:19:29 -0700 Subject: [PATCH 048/446] remove usings. --- src/Avalonia.OpenGL/Angle/AngleEglInterface.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs index b73053b52e..07890b260c 100644 --- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs +++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs @@ -1,7 +1,5 @@ using System; using System.Runtime.InteropServices; -using Avalonia.Platform; -using Avalonia.Platform.Interop; namespace Avalonia.OpenGL.Angle { From 0b7f211b3a9a98449fc0e6a6c6efd4a911645739 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:22:17 -0700 Subject: [PATCH 049/446] fix build error. --- src/Avalonia.OpenGL/Angle/AngleEglInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs index 07890b260c..0067124c0b 100644 --- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs +++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs @@ -24,7 +24,7 @@ namespace Avalonia.OpenGL.Angle throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point"); } - return eglGetProcAddress; + return EGL_GetProcAddress; } throw new PlatformNotSupportedException(); From 7dc21fa2164b376f064e12d8b00219c3f4c78791 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:29:30 -0700 Subject: [PATCH 050/446] only apply no-redirection bitmap if we are on windows 10. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index d964e490c0..a96a662a71 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -45,6 +45,7 @@ namespace Avalonia.Win32 private Thickness _extendedMargins; private Thickness _offScreenMargin; private double _extendTitleBarHint = -1; + private bool _isUsingComposition; #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; @@ -110,6 +111,7 @@ namespace Avalonia.Win32 var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); cgl.AttachToCompositionTree(_hwnd); _gl = cgl; + _isUsingComposition = true; } else { @@ -626,7 +628,7 @@ namespace Avalonia.Win32 protected virtual IntPtr CreateWindowOverride(ushort atom) { return CreateWindowEx( - (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP, // TODO this is only when using Win UI Comp. + _isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0, atom, null, (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN, From 094e605af9000e257eff19b0657b4db217527bf0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:36:46 -0700 Subject: [PATCH 051/446] ensure dx11 api for win.ui.comp. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index a96a662a71..fb9f7783b1 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -7,6 +7,7 @@ using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.OpenGL; +using Avalonia.OpenGL.Angle; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Win32.Input; @@ -106,11 +107,15 @@ namespace Avalonia.Win32 if (Win32GlManager.EglFeature != null) { - if (Win32Platform.WindowsVersion.Major >= 10) + if (Win32Platform.WindowsVersion.Major >= 10 && + Win32GlManager.EglFeature.Display is AngleWin32EglDisplay angleDisplay && + angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11) { var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); cgl.AttachToCompositionTree(_hwnd); + _gl = cgl; + _isUsingComposition = true; } else From 874ac50de24753be252f42a237250137fff14447 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:54:31 -0700 Subject: [PATCH 052/446] add a texture wrapping method without requiring offset, etc. --- src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs index 3b254d401a..9bb0dd8351 100644 --- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs +++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs @@ -78,6 +78,13 @@ namespace Avalonia.OpenGL.Angle return d3dDeviceHandle; } + public EglSurface WrapDirect3D11Texture(IntPtr handle) + { + if (PlatformApi != AngleOptions.PlatformApi.DirectX11) + throw new InvalidOperationException("Current platform API is " + PlatformApi); + return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_NONE }); + } + public EglSurface WrapDirect3D11Texture(IntPtr handle, int offsetX, int offsetY, int width, int height) { if (PlatformApi != AngleOptions.PlatformApi.DirectX11) From fdb68480463776f8d5cd125ce4eb867a5a9ebc75 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 02:55:13 -0700 Subject: [PATCH 053/446] check windows 10 build number and set composition flag before create window is called. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index fb9f7783b1..c100b3308d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -101,15 +101,16 @@ namespace Avalonia.Win32 }; _rendererLock = new ManagedDeferredRendererLock(); + _isUsingComposition = Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 14393 && + Win32GlManager.EglFeature.Display is AngleWin32EglDisplay angleDisplay && + angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; CreateWindow(); _framebuffer = new FramebufferManager(_hwnd); if (Win32GlManager.EglFeature != null) { - if (Win32Platform.WindowsVersion.Major >= 10 && - Win32GlManager.EglFeature.Display is AngleWin32EglDisplay angleDisplay && - angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11) + if (_isUsingComposition) { var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); cgl.AttachToCompositionTree(_hwnd); From d25d88cdfac7ab224b3fb6f5342699c262a5fabd Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 14 Sep 2020 04:20:17 -0700 Subject: [PATCH 054/446] increase min windows version. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index c100b3308d..7222b66beb 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -101,7 +101,7 @@ namespace Avalonia.Win32 }; _rendererLock = new ManagedDeferredRendererLock(); - _isUsingComposition = Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 14393 && + _isUsingComposition = Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && Win32GlManager.EglFeature.Display is AngleWin32EglDisplay angleDisplay && angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; From 974c73ca46c414899535635ffab73930e53b5924 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 15 Sep 2020 02:36:27 -0700 Subject: [PATCH 055/446] opengl es 3 --- src/Avalonia.OpenGL/EglDisplay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 7b194e4346..a3d11f2311 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -67,12 +67,13 @@ namespace Avalonia.OpenGL { Attributes = new[] { - EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE }, Api = EGL_OPENGL_ES_API, - RenderableTypeBit = EGL_OPENGL_ES2_BIT, - Version = new GlVersion(GlProfileType.OpenGLES, 2, 0) + RenderableTypeBit = EGL_OPENGL_ES3_BIT, + Version = new GlVersion(GlProfileType.OpenGLES, 3, 0) } }) { From 1d1275059883ba603db9727422b5c10471776d8b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 16 Sep 2020 03:25:47 -0700 Subject: [PATCH 056/446] allow enabling of windows.ui.composition with win32 options. --- src/Windows/Avalonia.Win32/Win32Platform.cs | 9 +++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index af6058d197..7f1ac443ad 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -39,6 +39,15 @@ namespace Avalonia public bool AllowEglInitialization { get; set; } = true; public bool? EnableMultitouch { get; set; } public bool OverlayPopups { get; set; } + + /// + /// Render Avalonia to a Texture inside the Windows.UI.Composition tree. + /// + /// + /// Supported on Windows 10 build 16299 and above. Ignored on other versions. + /// This is recommended if you need to use AcrylicBlur or acrylic in your applications. + /// + public bool UseWindowsUIComposition { get; set; } } } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 7222b66beb..a2bd7f57b9 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -101,7 +101,8 @@ namespace Avalonia.Win32 }; _rendererLock = new ManagedDeferredRendererLock(); - _isUsingComposition = Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && + _isUsingComposition = Win32Platform.Options.UseWindowsUIComposition && + Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && Win32GlManager.EglFeature.Display is AngleWin32EglDisplay angleDisplay && angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; From 8470a399b6214ecdd82774cbbc7a1620751db787 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Sep 2020 14:52:02 -0700 Subject: [PATCH 057/446] point angle interface to avalonias custom built optimised angle. --- samples/ControlCatalog.NetCore/Program.cs | 4 +++- src/Avalonia.OpenGL/Angle/AngleEglInterface.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 142736a0bb..9b62c2c98e 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -13,6 +13,7 @@ using Avalonia.Skia; using Avalonia.ReactiveUI; using Avalonia.Threading; using Avalonia.Dialogs; +using Avalonia.OpenGL; namespace ControlCatalog.NetCore { @@ -116,7 +117,8 @@ namespace ControlCatalog.NetCore .With(new Win32PlatformOptions { EnableMultitouch = true, - AllowEglInitialization = true + AllowEglInitialization = true, + UseWindowsUIComposition = true }) .UseSkia() .UseReactiveUI() diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs index 0067124c0b..a6f5f33968 100644 --- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs +++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs @@ -5,7 +5,7 @@ namespace Avalonia.OpenGL.Angle { public class AngleEglInterface : EglInterface { - [DllImport("avangle.dll", CharSet = CharSet.Ansi)] + [DllImport("av_libGLESv2.dll", CharSet = CharSet.Ansi)] static extern IntPtr EGL_GetProcAddress(string proc); public AngleEglInterface() : base(LoadAngle()) From a114c59fe7930c523f4503c0bd401ee7e0f8bac6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Sep 2020 16:09:59 -0700 Subject: [PATCH 058/446] update angle binaries. --- samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 28b0257eda..6b3e3a5f61 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -13,7 +13,7 @@ - + From d3700adb560d295908c87231dc1b77ff973c4d7f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Sep 2020 16:23:40 -0700 Subject: [PATCH 059/446] setup the acrylic blur in a way it can be show or hidden. --- .../Avalonia.Win32/Composition/CompositionHost.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 8da205fdc6..958959bc93 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -76,7 +76,15 @@ namespace Avalonia.Win32 visual.Brush = brush; visual.Offset = new System.Numerics.Vector3(0, 0, 0); - target.Root = CreateBlur(); + + var container = _compositor.CreateContainerVisual(); + container.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1); + + target.Root = container; + + var blur = CreateBlur(); + + container.Children.InsertAtTop(blur); var visuals = target.Root.As().Children; From e00aa6e5ded72e32e356793c38bb68daeb2f127c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Sep 2020 17:30:13 -0700 Subject: [PATCH 060/446] iscorrupted no longer useful. --- .../Composition/CompositionEglGlPlatformSurface.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index 701070a6b5..96646b9590 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -35,7 +35,7 @@ namespace Avalonia.Win32 { private readonly EglDisplay _display; private readonly IEglWindowGlPlatformSurfaceInfo _info; - private PixelSize _initialSize; + private readonly PixelSize _initialSize; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); private Windows.UI.Composition.Visual _compositionVisual; @@ -56,7 +56,7 @@ namespace Avalonia.Win32 _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); } - public override bool IsCorrupted => _initialSize != _info.Size; + public override bool IsCorrupted => false; public override IGlPlatformSurfaceRenderingSession BeginDraw() { var offset = new POINT(); From 6bcf36e23fc61353406ee1dcb6009fe8cba6aa65 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 01:36:17 -0700 Subject: [PATCH 061/446] windows.ui.composition compatible with acrylic blur setting. --- .../CompositionEglGlPlatformSurface.cs | 8 +- .../Composition/CompositionHost.cs | 24 +++++- src/Windows/Avalonia.Win32/WindowImpl.cs | 84 +++++++++++-------- 3 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index 96646b9590..38ea545957 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -6,7 +6,7 @@ using Windows.UI.Composition.Interop; namespace Avalonia.Win32 { - public class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase + internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase { private readonly EglDisplay _display; private readonly EglContext _context; @@ -21,9 +21,11 @@ namespace Avalonia.Win32 _info = info; } - public void AttachToCompositionTree(IntPtr hwnd) + public IBlurHost AttachToCompositionTree(IntPtr hwnd) { - _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd, out _surface); + _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); + + return blurHost; } public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 958959bc93..dfcf6e1d7f 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -7,6 +7,26 @@ using WinRT; namespace Avalonia.Win32 { + internal interface IBlurHost + { + void SetBlur(bool enabled); + } + + internal class CompositionBlurHost : IBlurHost + { + Windows.UI.Composition.Visual _blurVisual; + + public CompositionBlurHost(Windows.UI.Composition.Visual blurVisual) + { + _blurVisual = blurVisual; + } + + public void SetBlur(bool enabled) + { + _blurVisual.IsVisible = enabled; + } + } + class CompositionHost { internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE @@ -61,7 +81,7 @@ namespace Avalonia.Win32 _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); } - public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual) + public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual, out IBlurHost blurHost) { var target = CreateDesktopWindowTarget(hwnd); @@ -84,6 +104,8 @@ namespace Avalonia.Win32 var blur = CreateBlur(); + blurHost = new CompositionBlurHost(blur); + container.Children.InsertAtTop(blur); var visuals = target.Root.As().Children; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index a2bd7f57b9..46c18dea68 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -47,6 +47,7 @@ namespace Avalonia.Win32 private Thickness _offScreenMargin; private double _extendTitleBarHint = -1; private bool _isUsingComposition; + private IBlurHost _blurHost; #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; @@ -114,7 +115,7 @@ namespace Avalonia.Win32 if (_isUsingComposition) { var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); - cgl.AttachToCompositionTree(_hwnd); + _blurHost = cgl.AttachToCompositionTree(_hwnd); _gl = cgl; @@ -341,54 +342,63 @@ namespace Avalonia.Win32 private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel) { - bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628; - - var accent = new AccentPolicy(); - var accentStructSize = Marshal.SizeOf(accent); - - if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic) + if (_isUsingComposition) { - transparencyLevel = WindowTransparencyLevel.Blur; - } + _blurHost?.SetBlur(transparencyLevel >= WindowTransparencyLevel.Blur); - switch (transparencyLevel) + return transparencyLevel; + } + else { - default: - case WindowTransparencyLevel.None: - accent.AccentState = AccentState.ACCENT_DISABLED; - break; + bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628; - case WindowTransparencyLevel.Transparent: - accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT; - break; + var accent = new AccentPolicy(); + var accentStructSize = Marshal.SizeOf(accent); - case WindowTransparencyLevel.Blur: - accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND; - break; + if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic) + { + transparencyLevel = WindowTransparencyLevel.Blur; + } - case WindowTransparencyLevel.AcrylicBlur: - case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic. - accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC; - transparencyLevel = WindowTransparencyLevel.AcrylicBlur; - break; - } + switch (transparencyLevel) + { + default: + case WindowTransparencyLevel.None: + accent.AccentState = AccentState.ACCENT_DISABLED; + break; + + case WindowTransparencyLevel.Transparent: + accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT; + break; + + case WindowTransparencyLevel.Blur: + accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND; + break; + + case WindowTransparencyLevel.AcrylicBlur: + case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic. + accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC; + transparencyLevel = WindowTransparencyLevel.AcrylicBlur; + break; + } - accent.AccentFlags = 2; - accent.GradientColor = 0x01000000; + accent.AccentFlags = 2; + accent.GradientColor = 0x01000000; - var accentPtr = Marshal.AllocHGlobal(accentStructSize); - Marshal.StructureToPtr(accent, accentPtr, false); + var accentPtr = Marshal.AllocHGlobal(accentStructSize); + Marshal.StructureToPtr(accent, accentPtr, false); - var data = new WindowCompositionAttributeData(); - data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; - data.SizeOfData = accentStructSize; - data.Data = accentPtr; + var data = new WindowCompositionAttributeData(); + data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; + data.SizeOfData = accentStructSize; + data.Data = accentPtr; - SetWindowCompositionAttribute(_hwnd, ref data); + SetWindowCompositionAttribute(_hwnd, ref data); - Marshal.FreeHGlobal(accentPtr); + Marshal.FreeHGlobal(accentPtr); - return transparencyLevel; + return transparencyLevel; + } } public IEnumerable Surfaces => new object[] { Handle, _gl, _framebuffer }; From 8fa906281fa2b175247bd39db8b8b33acab8ee1f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 01:39:49 -0700 Subject: [PATCH 062/446] zero bitmap memory, not transparent white. --- src/Avalonia.Visuals/Rendering/RenderLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Visuals/Rendering/RenderLayer.cs index ddf5f4e5cf..668e9167dc 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLayer.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLayer.cs @@ -35,7 +35,7 @@ namespace Avalonia.Rendering using (var context = resized.Item.CreateDrawingContext(null)) { - context.Clear(Colors.Transparent); + context.Clear(default); Bitmap = resized; Scaling = scaling; From 274e76fec9211b016d53750cc202835549adbf1c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 01:49:48 -0700 Subject: [PATCH 063/446] dispose current bitmap whilst context is current. --- src/Avalonia.Visuals/Rendering/RenderLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Visuals/Rendering/RenderLayer.cs index 668e9167dc..1fbd8c02c0 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLayer.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLayer.cs @@ -30,11 +30,11 @@ namespace Avalonia.Rendering { if (Size != size || Scaling != scaling) { - Bitmap.Dispose(); var resized = RefCountable.Create(drawingContext.CreateLayer(size)); using (var context = resized.Item.CreateDrawingContext(null)) { + Bitmap.Dispose(); context.Clear(default); Bitmap = resized; From 7f9b675efbe154439a956504464883fffae7bbd5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 03:11:42 -0700 Subject: [PATCH 064/446] refactor effects. --- .../Composition/CompositionHost.cs | 18 ++++++-- .../Avalonia.Win32/Composition/EffectBase.cs | 45 +++++++++++++++++++ .../Composition/GaussianBlurEffect.cs | 42 +++++------------ .../Composition/SaturationEffect.cs | 35 +++++++++++++++ 4 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/Composition/EffectBase.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index dfcf6e1d7f..f1cf5af0e8 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -121,18 +121,28 @@ namespace Avalonia.Win32 public SpriteVisual CreateBlur() { - var effect = new GaussianBlurEffect(); - var effectFactory = _compositor.CreateEffectFactory(effect); - var blurBrush = effectFactory.CreateBrush(); + var blurEffect = new GaussianBlurEffect(new CompositionEffectSourceParameter ("backdrop")); + var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect); + var blurBrush = blurEffectFactory.CreateBrush(); var backDropBrush = _compositor.CreateBackdropBrush(); blurBrush.SetSourceParameter("backdrop", backDropBrush); + + var saturateEffect = new SaturationEffect(blurEffect); + var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); + + + var satBrush = satEffectFactory.CreateBrush(); + satBrush.SetSourceParameter("backdrop", backDropBrush); + + + var visual = _compositor.CreateSpriteVisual(); visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); - visual.Brush = blurBrush; + visual.Brush = satBrush; return visual; } diff --git a/src/Windows/Avalonia.Win32/Composition/EffectBase.cs b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs new file mode 100644 index 0000000000..ca5b15971e --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs @@ -0,0 +1,45 @@ +using System; +using Windows.Graphics.Effects; +using Windows.Graphics.Effects.Interop; + + +namespace Avalonia.Win32 +{ + abstract class EffectBase : IGraphicsEffect, IGraphicsEffectSource, global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop + { + private IGraphicsEffectSource[] _sources; + + public EffectBase(params IGraphicsEffectSource[] sources) + { + _sources = sources; + } + + private IGraphicsEffectSource _source; + + public virtual string Name { get; set; } + + public abstract Guid EffectId { get; } + + public abstract uint PropertyCount { get; } + + public uint SourceCount => (uint)_sources.Length; + + public IGraphicsEffectSource GetSource(uint index) + { + if(index < _sources.Length) + { + return _sources[index]; + } + + return null; + } + + public uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping) + { + throw new NotImplementedException(); + } + + public abstract object GetProperty(uint index); + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs index 4e1be3af1a..342e68eeb4 100644 --- a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs +++ b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs @@ -1,13 +1,14 @@ using System; using Windows.Graphics.Effects; -using Windows.Graphics.Effects.Interop; -using Windows.UI.Composition; - namespace Avalonia.Win32 { - class GaussianBlurEffect : IGraphicsEffect, IGraphicsEffectSource, global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop + class GaussianBlurEffect : EffectBase { + public GaussianBlurEffect(IGraphicsEffectSource source) : base(source) + { + } + enum D2D1_GAUSSIANBLUR_OPTIMIZATION { D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED, @@ -31,20 +32,11 @@ namespace Avalonia.Win32 D2D1_GAUSSIANBLUR_PROP_FORCE_DWORD }; - public string Name { get; set; } - - public Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur; + public override Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur; - public uint PropertyCount => 3; - - public uint SourceCount => 1; - - public uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping) - { - throw new NotImplementedException(); - } + public override uint PropertyCount => 3; - public object GetProperty(uint index) + public override object GetProperty(uint index) { switch ((D2D1GaussianBlurProp)index) { @@ -52,28 +44,14 @@ namespace Avalonia.Win32 return 30.0f; case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION: - return (UInt32)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED; + return (uint)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED; case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE: - return (UInt32)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD; + return (uint)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD; } return null; } - - private IGraphicsEffectSource _source = new CompositionEffectSourceParameter("backdrop"); - - public IGraphicsEffectSource GetSource(uint index) - { - if (index == 0) - { - return _source; - } - else - { - return null; - } - } } } diff --git a/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs new file mode 100644 index 0000000000..90eca22d8e --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs @@ -0,0 +1,35 @@ +using System; +using Windows.Graphics.Effects; + + +namespace Avalonia.Win32 +{ + class SaturationEffect : EffectBase + { + public SaturationEffect(IGraphicsEffect source) : base(source) + { + } + + enum D2D1_SATURATION_PROP + { + D2D1_SATURATION_PROP_SATURATION, + D2D1_SATURATION_PROP_FORCE_DWORD + }; + + public override Guid EffectId => D2DEffects.CLSID_D2D1Saturation; + + public override uint PropertyCount => 1; + + public override object GetProperty(uint index) + { + switch ((D2D1_SATURATION_PROP)index) + { + case D2D1_SATURATION_PROP.D2D1_SATURATION_PROP_SATURATION: + return 2.0f; + } + + return null; + } + } +} + From 57b3f0b10574a167a448ad4401b6de88c1563354 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 03:21:04 -0700 Subject: [PATCH 065/446] revert changed code. --- .../ControlCatalog.NetCore/Properties/launchSettings.json | 8 -------- samples/ControlCatalog/MainWindow.xaml | 8 ++++++-- 2 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 samples/ControlCatalog.NetCore/Properties/launchSettings.json diff --git a/samples/ControlCatalog.NetCore/Properties/launchSettings.json b/samples/ControlCatalog.NetCore/Properties/launchSettings.json deleted file mode 100644 index cd8a2c824b..0000000000 --- a/samples/ControlCatalog.NetCore/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "ControlCatalog.NetCore": { - "commandName": "Project", - "nativeDebugging": true - } - } -} \ No newline at end of file diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index eebc804a64..97bd88f5e4 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -7,8 +7,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ControlCatalog.ViewModels" xmlns:v="clr-namespace:ControlCatalog.Views" - TransparencyLevelHint="Transparent" - x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" Background="{x:Null}"> + ExtendClientAreaToDecorationsHint="{Binding ExtendClientAreaEnabled}" + ExtendClientAreaChromeHints="{Binding ChromeHints}" + ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}" + TransparencyLevelHint="{Binding TransparencyLevel}" + x:Name="MainWindow" + x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"> From 4d0c27dc8f8da04be98b690a8406df2d86a9f49a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 03:32:43 -0700 Subject: [PATCH 066/446] whitespace. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 46c18dea68..9c002038cb 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -717,7 +717,7 @@ namespace Avalonia.Win32 { _scaling = dpix / 96.0; } - } + } } private void CreateDropTarget() From 28168d571be4912cb85d87629fba798ae5fd3b09 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 10:06:19 -0700 Subject: [PATCH 067/446] make managed deferredrenderer lock global. --- src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs | 6 +++--- src/Windows/Avalonia.Win32/WindowImpl.cs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 25a34561fc..0bab0d1657 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -339,7 +339,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_PAINT: { - using (_rendererLock.Lock()) + using (s_rendererLock.Lock()) { if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) { @@ -356,7 +356,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_SIZE: { - using (_rendererLock.Lock()) + using (s_rendererLock.Lock()) { // Do nothing here, just block until the pending frame render is completed on the render thread } @@ -464,7 +464,7 @@ namespace Avalonia.Win32 } } - using (_rendererLock.Lock()) + using (s_rendererLock.Lock()) { return DefWindowProc(hWnd, msg, wParam, lParam); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9c002038cb..7b8b6d3e83 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -56,7 +56,7 @@ namespace Avalonia.Win32 private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); private readonly TouchDevice _touchDevice; private readonly MouseDevice _mouseDevice; - private readonly ManagedDeferredRendererLock _rendererLock; + private static readonly ManagedDeferredRendererLock s_rendererLock = new ManagedDeferredRendererLock(); private readonly FramebufferManager _framebuffer; private readonly IGlPlatformSurface _gl; @@ -100,7 +100,6 @@ namespace Avalonia.Win32 IsResizable = true, Decorations = SystemDecorations.Full }; - _rendererLock = new ManagedDeferredRendererLock(); _isUsingComposition = Win32Platform.Options.UseWindowsUIComposition && Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && @@ -443,7 +442,7 @@ namespace Avalonia.Win32 return customRendererFactory.Create(root, loop); return Win32Platform.UseDeferredRendering ? - (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) : + (IRenderer)new DeferredRenderer(root, loop, rendererLock: s_rendererLock) : new ImmediateRenderer(root); } From 79a53846c8bb7dbb789a3d08fe8179e8ada37b72 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Fri, 18 Sep 2020 22:35:59 +0500 Subject: [PATCH 068/446] InvariantCultureIgnoreCase -> OrdinalIgnoreCase --- .../Remote/HtmlTransport/HtmlTransport.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index de7da4985c..0996431b1f 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -267,11 +267,11 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport { var parts = message.Split(':'); var key = parts[0]; - if (key.Equals("frame-received", StringComparison.InvariantCultureIgnoreCase)) + if (key.Equals("frame-received", StringComparison.OrdinalIgnoreCase)) { return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }; } - else if (key.Equals("pointer-released", StringComparison.InvariantCultureIgnoreCase)) + else if (key.Equals("pointer-released", StringComparison.OrdinalIgnoreCase)) { return new InputProtocol.PointerReleasedEventMessage { @@ -281,7 +281,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport Button = ParseMouseButton(parts[4]), }; } - else if (key.Equals("pointer-pressed", StringComparison.InvariantCultureIgnoreCase)) + else if (key.Equals("pointer-pressed", StringComparison.OrdinalIgnoreCase)) { return new InputProtocol.PointerPressedEventMessage { @@ -291,7 +291,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport Button = ParseMouseButton(parts[4]), }; } - else if (key.Equals("pointer-moved", StringComparison.InvariantCultureIgnoreCase)) + else if (key.Equals("pointer-moved", StringComparison.OrdinalIgnoreCase)) { return new InputProtocol.PointerMovedEventMessage { @@ -300,7 +300,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport Y = ParseDouble(parts[3]), }; } - else if (key.Equals("scroll", StringComparison.InvariantCultureIgnoreCase)) + else if (key.Equals("scroll", StringComparison.OrdinalIgnoreCase)) { return new InputProtocol.ScrollEventMessage { @@ -316,7 +316,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport } private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) => - string.IsNullOrEmpty(modifiersText) + string.IsNullOrWhiteSpace(modifiersText) ? null : modifiersText .Split(',') From 9ce5a98277e7c33f8ec3e298a1669dd27485e1fd Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Fri, 18 Sep 2020 22:39:22 +0500 Subject: [PATCH 069/446] IsNullOrEmpty -> IsNullOrWhiteSpace --- .../Remote/HtmlTransport/HtmlTransport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 0996431b1f..804bb09510 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -325,7 +325,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => - string.IsNullOrEmpty(buttonText) + string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None : (InputProtocol.MouseButton)Enum.Parse( typeof(InputProtocol.MouseButton), buttonText, true); From 7720d8f9062c220e05b2c40183694defccccb616 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 10:42:39 -0700 Subject: [PATCH 070/446] fix visual tree so that content resizes with the window. --- .../Composition/CompositionEglGlPlatformSurface.cs | 3 ++- .../Avalonia.Win32/Composition/CompositionHost.cs | 11 ++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index 38ea545957..4e6dc79673 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -58,7 +58,8 @@ namespace Avalonia.Win32 _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); } - public override bool IsCorrupted => false; + public override bool IsCorrupted => _info.Size != _initialSize; + public override IGlPlatformSurfaceRenderingSession BeginDraw() { var offset = new POINT(); diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index f1cf5af0e8..615b5d303a 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -95,11 +95,10 @@ namespace Avalonia.Win32 var visual = _compositor.CreateSpriteVisual(); visual.Brush = brush; - visual.Offset = new System.Numerics.Vector3(0, 0, 0); + visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1); var container = _compositor.CreateContainerVisual(); - container.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1); - + target.Root = container; var blur = CreateBlur(); @@ -108,13 +107,11 @@ namespace Avalonia.Win32 container.Children.InsertAtTop(blur); - var visuals = target.Root.As().Children; - - visuals.InsertAtTop(visual); + container.Children.InsertAtTop(visual); visual.CompositeMode = CompositionCompositeMode.SourceOver; - surfaceVisual = visual; + surfaceVisual = container; return surfaceInterop; } From a004366e70c0e8b7dead53f092bc261246597a4d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 10:50:27 -0700 Subject: [PATCH 071/446] make blur not visible at startup. --- src/Windows/Avalonia.Win32/Composition/CompositionHost.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 615b5d303a..516ad44071 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -126,17 +126,14 @@ namespace Avalonia.Win32 blurBrush.SetSourceParameter("backdrop", backDropBrush); - var saturateEffect = new SaturationEffect(blurEffect); var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); - var satBrush = satEffectFactory.CreateBrush(); satBrush.SetSourceParameter("backdrop", backDropBrush); - - var visual = _compositor.CreateSpriteVisual(); + visual.IsVisible = false; visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); visual.Brush = satBrush; From 099909d4e286a9820e8a34b9b793979c4b827e77 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 10:55:07 -0700 Subject: [PATCH 072/446] make win.ui.comp the default when running on win10 and compatible gpu is used. --- samples/ControlCatalog.NetCore/Program.cs | 3 +-- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 9b62c2c98e..d967fde34c 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -117,8 +117,7 @@ namespace ControlCatalog.NetCore .With(new Win32PlatformOptions { EnableMultitouch = true, - AllowEglInitialization = true, - UseWindowsUIComposition = true + AllowEglInitialization = true }) .UseSkia() .UseReactiveUI() diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 7f1ac443ad..e9d34d7d56 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -47,7 +47,7 @@ namespace Avalonia /// Supported on Windows 10 build 16299 and above. Ignored on other versions. /// This is recommended if you need to use AcrylicBlur or acrylic in your applications. /// - public bool UseWindowsUIComposition { get; set; } + public bool UseWindowsUIComposition { get; set; } = true; } } From 59d68a30370694203a4647399cddc81542c09c92 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 10:57:45 -0700 Subject: [PATCH 073/446] remove usings. --- samples/ControlCatalog.NetCore/Program.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index d967fde34c..3851cbf9a6 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -7,13 +7,11 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Dialogs; using Avalonia.Headless; using Avalonia.LogicalTree; -using Avalonia.Skia; using Avalonia.ReactiveUI; using Avalonia.Threading; -using Avalonia.Dialogs; -using Avalonia.OpenGL; namespace ControlCatalog.NetCore { From 02f33d561c21439849838cce4587f27b5560122e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 10:59:16 -0700 Subject: [PATCH 074/446] 1 file per class. --- .../Composition/CompositionBlurHost.cs | 18 +++++++++++++++++ .../Composition/CompositionHost.cs | 20 ------------------- .../Avalonia.Win32/Composition/IBlurHost.cs | 8 ++++++++ 3 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs create mode 100644 src/Windows/Avalonia.Win32/Composition/IBlurHost.cs diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs new file mode 100644 index 0000000000..4c090e797c --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs @@ -0,0 +1,18 @@ +namespace Avalonia.Win32 +{ + internal class CompositionBlurHost : IBlurHost + { + Windows.UI.Composition.Visual _blurVisual; + + public CompositionBlurHost(Windows.UI.Composition.Visual blurVisual) + { + _blurVisual = blurVisual; + } + + public void SetBlur(bool enabled) + { + _blurVisual.IsVisible = enabled; + } + } +} + diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs index 516ad44071..07a0170810 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs @@ -7,26 +7,6 @@ using WinRT; namespace Avalonia.Win32 { - internal interface IBlurHost - { - void SetBlur(bool enabled); - } - - internal class CompositionBlurHost : IBlurHost - { - Windows.UI.Composition.Visual _blurVisual; - - public CompositionBlurHost(Windows.UI.Composition.Visual blurVisual) - { - _blurVisual = blurVisual; - } - - public void SetBlur(bool enabled) - { - _blurVisual.IsVisible = enabled; - } - } - class CompositionHost { internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE diff --git a/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs new file mode 100644 index 0000000000..6ab470d81c --- /dev/null +++ b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs @@ -0,0 +1,8 @@ +namespace Avalonia.Win32 +{ + internal interface IBlurHost + { + void SetBlur(bool enabled); + } +} + From 3541e70c3b4c822ca8a082c76c45efaae752d37d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 11:50:45 -0700 Subject: [PATCH 075/446] fix hostapp for designer. --- .../Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index 51d18e55d1..152581de27 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -23,5 +23,8 @@ + + + From 8c667faf64f9adf0829929a2a70a9e9247a3d931 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 18 Sep 2020 13:37:16 -0700 Subject: [PATCH 076/446] use stable Angle. --- samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 2 +- samples/interop/NativeEmbedSample/NativeEmbedSample.csproj | 2 +- src/Windows/Avalonia.Win32/Avalonia.Win32.csproj | 2 +- .../Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 6b3e3a5f61..d4d50163d5 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -13,7 +13,7 @@ - + diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj index cc831ef8ae..abdcac3b77 100644 --- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj +++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj @@ -13,7 +13,7 @@ - + Designer diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index c6af049868..5889d919df 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index 152581de27..a2d27fd579 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -24,7 +24,7 @@ - + From 10206e728dacd64f4b754965ad6555ee6ac6a6bc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 27 Sep 2020 21:26:25 +0100 Subject: [PATCH 077/446] Revert "make managed deferredrenderer lock global." This reverts commit 28168d571be4912cb85d87629fba798ae5fd3b09. --- src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs | 6 +++--- src/Windows/Avalonia.Win32/WindowImpl.cs | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 0bab0d1657..25a34561fc 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -339,7 +339,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_PAINT: { - using (s_rendererLock.Lock()) + using (_rendererLock.Lock()) { if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) { @@ -356,7 +356,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_SIZE: { - using (s_rendererLock.Lock()) + using (_rendererLock.Lock()) { // Do nothing here, just block until the pending frame render is completed on the render thread } @@ -464,7 +464,7 @@ namespace Avalonia.Win32 } } - using (s_rendererLock.Lock()) + using (_rendererLock.Lock()) { return DefWindowProc(hWnd, msg, wParam, lParam); } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 6ca12e571f..848635fe23 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -58,7 +58,7 @@ namespace Avalonia.Win32 private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); private readonly TouchDevice _touchDevice; private readonly MouseDevice _mouseDevice; - private static readonly ManagedDeferredRendererLock s_rendererLock = new ManagedDeferredRendererLock(); + private readonly ManagedDeferredRendererLock _rendererLock; private readonly FramebufferManager _framebuffer; private readonly IGlPlatformSurface _gl; @@ -102,6 +102,7 @@ namespace Avalonia.Win32 IsResizable = true, Decorations = SystemDecorations.Full }; + _rendererLock = new ManagedDeferredRendererLock(); _isUsingComposition = Win32Platform.Options.UseWindowsUIComposition && Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && @@ -445,7 +446,7 @@ namespace Avalonia.Win32 return customRendererFactory.Create(root, loop); return Win32Platform.UseDeferredRendering ? - (IRenderer)new DeferredRenderer(root, loop, rendererLock: s_rendererLock) : + (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) : new ImmediateRenderer(root); } From 05f7ee83af016d4f6f10b9294e270299295596e9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 29 Sep 2020 18:23:19 +0100 Subject: [PATCH 078/446] fix resizing of composition surface. --- .../Composition/CompositionEglGlPlatformSurface.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index c034d8f7f7..2e4b69c0b5 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -39,7 +39,7 @@ namespace Avalonia.Win32 { private readonly EglPlatformOpenGlInterface _egl; private readonly IEglWindowGlPlatformSurfaceInfo _info; - private readonly PixelSize _initialSize; + private PixelSize _currentSize; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); private Windows.UI.Composition.Visual _compositionVisual; @@ -53,7 +53,7 @@ namespace Avalonia.Win32 _egl = egl; _surfaceInterop = interopSurface; _info = info; - _initialSize = info.Size; + _currentSize = info.Size; _compositionVisual = compositionVisual; _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); @@ -61,6 +61,13 @@ namespace Avalonia.Win32 public override IGlPlatformSurfaceRenderingSession BeginDraw() { + if (_info.Size != _currentSize) + { + _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); + _currentSize = _info.Size; + } + var offset = new POINT(); _surfaceInterop.BeginDraw( From b5c5cc7192b8298c6136f331e3256a58a778256b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 2 Oct 2020 20:27:21 +0100 Subject: [PATCH 079/446] protect d3d calls. --- .../CompositionEglGlPlatformSurface.cs | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index 2e4b69c0b5..b30043fedd 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -25,9 +25,11 @@ namespace Avalonia.Win32 public IBlurHost AttachToCompositionTree(IntPtr hwnd) { - _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); - - return blurHost; + using (_egl.PrimaryContext.MakeCurrent()) + { + _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); + return blurHost; + } } public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() @@ -55,27 +57,38 @@ namespace Avalonia.Win32 _info = info; _currentSize = info.Size; _compositionVisual = compositionVisual; - _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + + using (_egl.PrimaryContext.MakeCurrent()) + { + _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + } + _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); } public override IGlPlatformSurfaceRenderingSession BeginDraw() { - if (_info.Size != _currentSize) + IntPtr texture; + EglSurface surface; + + using (_egl.PrimaryEglContext.EnsureCurrent()) { - _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); - _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); - _currentSize = _info.Size; - } + if (_info.Size != _currentSize) + { + _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); + _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); + _currentSize = _info.Size; + } - var offset = new POINT(); + var offset = new POINT(); - _surfaceInterop.BeginDraw( - IntPtr.Zero, - ref s_Iid, - out IntPtr texture, ref offset); + _surfaceInterop.BeginDraw( + IntPtr.Zero, + ref s_Iid, + out texture, ref offset); - var surface = (_egl.Display as AngleWin32EglDisplay).WrapDirect3D11Texture(_egl, texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); + surface = (_egl.Display as AngleWin32EglDisplay).WrapDirect3D11Texture(_egl, texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); + } return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); } From 754da3470ed5bb5d8e5084be88d5877503596672 Mon Sep 17 00:00:00 2001 From: Murdo R Ergeaux Date: Sat, 3 Oct 2020 08:35:19 +0100 Subject: [PATCH 080/446] Hardcode DatePicker month/day to prevent unrepresentable dates --- src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index daee7968eb..a0e8c03195 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -486,9 +486,9 @@ namespace Avalonia.Controls.Primitives switch (panelType) { case DateTimePickerPanelType.Year: - return new DateTime(value, FormatDate.Month, FormatDate.Day).ToString(ItemFormat); + return new DateTime(value, 1, 1).ToString(ItemFormat); case DateTimePickerPanelType.Month: - return new DateTime(FormatDate.Year, value, FormatDate.Day).ToString(ItemFormat); + return new DateTime(FormatDate.Year, value, 1).ToString(ItemFormat); case DateTimePickerPanelType.Day: return new DateTime(FormatDate.Year, FormatDate.Month, value).ToString(ItemFormat); case DateTimePickerPanelType.Hour: From 24b2615dd0378e0688d0dc2cc5bba8828a4ee40a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 28 Sep 2020 18:13:23 +0200 Subject: [PATCH 081/446] Fiexes issue #4657 --- src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml index 3623e95597..7dd4ed0832 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml @@ -12,6 +12,9 @@ + From 8bb8f3c79fed45ec03398b56bb4202a06b94745a Mon Sep 17 00:00:00 2001 From: "oliver.holliday@lontra.co.uk" Date: Wed, 7 Oct 2020 20:03:08 +0100 Subject: [PATCH 082/446] Add Skia support for Conic Gradients. Add a place holder implementation for Direct2D using SolidColorBrushImpl. --- .../Media/ConicGradientBrush.cs | 55 ++++++ .../Media/IConicGradientBrush.cs | 19 ++ .../Immutable/ImmutableConicGradientBrush.cs | 47 +++++ src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 17 ++ .../Media/DrawingContextImpl.cs | 6 + .../Media/SolidColorBrushImpl.cs | 17 ++ .../Media/ConicGradientBrushTests.cs | 178 ++++++++++++++++++ .../ConicGradientBrush_RedBlue.expected.png | Bin 0 -> 601 bytes ...cGradientBrush_RedBlue_Center.expected.png | Bin 0 -> 601 bytes ...h_RedBlue_Center_and_Rotation.expected.png | Bin 0 -> 601 bytes ...radientBrush_RedBlue_Rotation.expected.png | Bin 0 -> 601 bytes ...radientBrush_RedBlue_SoftEdge.expected.png | Bin 0 -> 601 bytes .../ConicGradientBrush_Umbrella.expected.png | Bin 0 -> 601 bytes .../ConicGradientBrush_RedBlue.expected.png | Bin 0 -> 7249 bytes ...cGradientBrush_RedBlue_Center.expected.png | Bin 0 -> 6797 bytes ...h_RedBlue_Center_and_Rotation.expected.png | Bin 0 -> 6792 bytes ...radientBrush_RedBlue_Rotation.expected.png | Bin 0 -> 7232 bytes ...radientBrush_RedBlue_SoftEdge.expected.png | Bin 0 -> 8805 bytes .../ConicGradientBrush_Umbrella.expected.png | Bin 0 -> 11408 bytes 19 files changed, 339 insertions(+) create mode 100644 src/Avalonia.Visuals/Media/ConicGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/IConicGradientBrush.cs create mode 100644 src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs create mode 100644 tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png create mode 100644 tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png diff --git a/src/Avalonia.Visuals/Media/ConicGradientBrush.cs b/src/Avalonia.Visuals/Media/ConicGradientBrush.cs new file mode 100644 index 0000000000..7c1266fa17 --- /dev/null +++ b/src/Avalonia.Visuals/Media/ConicGradientBrush.cs @@ -0,0 +1,55 @@ +using Avalonia.Media.Immutable; + +namespace Avalonia.Media +{ + /// + /// Paints an area with a swept circular gradient. + /// + public sealed class ConicGradientBrush : GradientBrush, IConicGradientBrush + { + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterProperty = + AvaloniaProperty.Register( + nameof(Center), + RelativePoint.Center); + + /// + /// Defines the property. + /// + public static readonly StyledProperty AngleProperty = + AvaloniaProperty.Register( + nameof(Angle), + 0); + + static ConicGradientBrush() + { + AffectsRender(CenterProperty, AngleProperty); + } + + /// + /// Gets or sets the center point of the gradient. + /// + public RelativePoint Center + { + get { return GetValue(CenterProperty); } + set { SetValue(CenterProperty, value); } + } + + /// + /// Gets or sets the angle of the start and end of the sweep, measured from above the center point. + /// + public double Angle + { + get { return GetValue(AngleProperty); } + set { SetValue(AngleProperty, value); } + } + + /// + public override IBrush ToImmutable() + { + return new ImmutableConicGradientBrush(this); + } + } +} diff --git a/src/Avalonia.Visuals/Media/IConicGradientBrush.cs b/src/Avalonia.Visuals/Media/IConicGradientBrush.cs new file mode 100644 index 0000000000..5368dd1851 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IConicGradientBrush.cs @@ -0,0 +1,19 @@ +namespace Avalonia.Media +{ + /// + /// Paints an area with a conic gradient. + /// + public interface IConicGradientBrush : IGradientBrush + { + /// + /// Gets the center point for the gradient. + /// + RelativePoint Center { get; } + + /// + /// Gets the starting angle for the gradient in degrees, measured from + /// the point above the center point. + /// + double Angle { get; } + } +} diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs new file mode 100644 index 0000000000..d3c80dfcad --- /dev/null +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace Avalonia.Media.Immutable +{ + /// + /// A brush that draws with a sweep gradient. + /// + public class ImmutableConicGradientBrush : ImmutableGradientBrush, IConicGradientBrush + { + /// + /// Initializes a new instance of the class. + /// + /// The gradient stops. + /// The opacity of the brush. + /// The spread method. + /// The center point for the gradient. + /// The starting angle for the gradient. + public ImmutableConicGradientBrush( + IReadOnlyList gradientStops, + double opacity = 1, + GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, + RelativePoint? center = null, + double angle = 0) + : base(gradientStops, opacity, spreadMethod) + { + Center = center ?? RelativePoint.Center; + Angle = angle; + } + + /// + /// Initializes a new instance of the class. + /// + /// The brush from which this brush's properties should be copied. + public ImmutableConicGradientBrush(ConicGradientBrush source) + : base(source) + { + Center = source.Center; + Angle = source.Angle; + } + + /// + public RelativePoint Center { get; } + + /// + public double Angle { get; } + } +} diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 05a8daaec8..b8c81b3409 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -624,6 +624,23 @@ namespace Avalonia.Skia } } + break; + } + case IConicGradientBrush conicGradient: + { + var center = conicGradient.Center.ToPixels(targetSize).ToSKPoint(); + + // Skia's default is that angle 0 is from the right hand side of the center point + // but we are matching CSS where the vertical point above the center is 0. + var angle = (float)(conicGradient.Angle - 90); + var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y); + + using (var shader = + SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation)) + { + paintWrapper.Paint.Shader = shader; + } + break; } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 258a51db5a..5d20bff1e9 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -424,6 +424,7 @@ namespace Avalonia.Direct2D1.Media var solidColorBrush = brush as ISolidColorBrush; var linearGradientBrush = brush as ILinearGradientBrush; var radialGradientBrush = brush as IRadialGradientBrush; + var conicGradientBrush = brush as IConicGradientBrush; var imageBrush = brush as IImageBrush; var visualBrush = brush as IVisualBrush; @@ -439,6 +440,11 @@ namespace Avalonia.Direct2D1.Media { return new RadialGradientBrushImpl(radialGradientBrush, _deviceContext, destinationSize); } + else if (conicGradientBrush != null) + { + // there is no Direct2D implementation of Conic Gradients so use Radial as a stand-in + return new SolidColorBrushImpl(conicGradientBrush, _deviceContext); + } else if (imageBrush?.Source != null) { return new ImageBrushImpl( diff --git a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs index f93b4a2e08..fea1ca9157 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs @@ -16,5 +16,22 @@ namespace Avalonia.Direct2D1.Media } ); } + + /// + /// Direct2D has no ConicGradient implementation so fall back to a solid colour brush based on + /// the first gradient stop. + /// + public SolidColorBrushImpl(IConicGradientBrush brush, SharpDX.Direct2D1.DeviceContext target) + { + PlatformBrush = new SharpDX.Direct2D1.SolidColorBrush( + target, + brush?.GradientStops[0].Color.ToDirect2D() ?? new SharpDX.Mathematics.Interop.RawColor4(), + new SharpDX.Direct2D1.BrushProperties + { + Opacity = brush != null ? (float)brush.Opacity : 1.0f, + Transform = target.Transform + } + ); + } } } diff --git a/tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs b/tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs new file mode 100644 index 0000000000..db69a0a028 --- /dev/null +++ b/tests/Avalonia.RenderTests/Media/ConicGradientBrushTests.cs @@ -0,0 +1,178 @@ +using Avalonia.Controls; +using Avalonia.Media; +using System.Threading.Tasks; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests +#else +namespace Avalonia.Direct2D1.RenderTests.Media +#endif +{ + public class ConicGradientBrushTests : TestBase + { + public ConicGradientBrushTests() : base(@"Media\ConicGradientBrush") + { + } + + [Fact] + public async Task ConicGradientBrush_RedBlue() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_Rotation() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + Angle = 90 + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_Center() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + Center = new RelativePoint(0.25, 0.25, RelativeUnit.Relative) + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_Center_and_Rotation() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + Center = new RelativePoint(0.25, 0.25, RelativeUnit.Relative), + Angle = 90 + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_RedBlue_SoftEdge() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Blue, Offset = 0.5 }, + new GradientStop { Color = Colors.Red, Offset = 1 }, + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + [Fact] + public async Task ConicGradientBrush_Umbrella() + { + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 200, + Height = 200, + Child = new Border + { + Background = new ConicGradientBrush + { + GradientStops = + { + new GradientStop { Color = Colors.Red, Offset = 0 }, + new GradientStop { Color = Colors.Yellow, Offset = 0.1667 }, + new GradientStop { Color = Colors.Lime, Offset = 0.3333 }, + new GradientStop { Color = Colors.Aqua, Offset = 0.5000 }, + new GradientStop { Color = Colors.Blue, Offset = 0.6667 }, + new GradientStop { Color = Colors.Magenta, Offset = 0.8333 }, + new GradientStop { Color = Colors.Red, Offset = 1 }, + } + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + } +} diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png b/tests/TestFiles/Direct2D1/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8e08d02f66d15179e5fd004f39bb13cbd4fe274f GIT binary patch literal 601 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yF%}28J29*~C-V}>VM%xNb!1@J z*w6hZk(GggiP_V|F{C2y?d5}<4F(J>2Q&WLuJ)K$$lYNq6T@l^)Bpr#rDy(^>}|HS vWlUjyUX$RcAi%;jN(}Wd07osue&b`j+h#Kt7)_eY2omvh^>bP0l+XkKIR;Xb literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..563bbfcc611681e14d54f391c617f12e3ed7e7e1 GIT binary patch literal 7249 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Center_and_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a8719e2e704e51f328ef90998babfdfc4cc5845b GIT binary patch literal 6792 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_Rotation.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..1c79db413bb90fee0331107c23578aae6089c841 GIT binary patch literal 7232 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_RedBlue_SoftEdge.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf8eddd81ad5984cd8124fecc2bc2ccbea94fad GIT binary patch literal 8805 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 ROvxLe(uF>R)ZDiZ`+qS-*r5Ob literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png b/tests/TestFiles/Skia/Media/ConicGradientBrush/ConicGradientBrush_Umbrella.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..98dfda782dcd436656c74249ec05013b7b6c3608 GIT binary patch literal 11408 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 Date: Thu, 8 Oct 2020 22:18:41 +0100 Subject: [PATCH 083/446] change surface origin. --- src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs index 9ee8d698ba..24464c39d9 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs @@ -93,7 +93,7 @@ namespace Avalonia.Skia var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8, new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat())); Surface = SKSurface.Create(_grContext, target, - GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888); + GRSurfaceOrigin.TopLeft, SKColorType.Rgba8888); CanBlit = gl.BlitFramebuffer != null; } From 8fddd4b94e19f3434868bd3367e83a6914a7b0cf Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Oct 2020 22:51:48 +0100 Subject: [PATCH 084/446] remove unused var. --- .../Composition/CompositionEglGlPlatformSurface.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index b30043fedd..547e2d3c0e 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -10,15 +10,13 @@ namespace Avalonia.Win32 { internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase { - private readonly EglDisplay _display; private EglPlatformOpenGlInterface _egl; private readonly IEglWindowGlPlatformSurfaceInfo _info; private ICompositionDrawingSurfaceInterop _surfaceInterop; - private Windows.UI.Composition.Visual _surface; + private Windows.UI.Composition.Visual _surface; public CompositionEglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base() - { - _display = egl.Display; + { _egl = egl; _info = info; } From 7b0598559a1307ebf2eab4da53e11d05884582ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 6 Oct 2020 12:06:01 +0200 Subject: [PATCH 085/446] GeometryDrawing does not need to allocate new Pen for each GetBounds call. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 4 +++- src/Avalonia.Visuals/Media/Geometry.cs | 4 ++-- src/Avalonia.Visuals/Media/GeometryDrawing.cs | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 7fb44152c9..2d00d82a46 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -2,6 +2,8 @@ Compat issues with assembly Avalonia.Visuals: MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.Geometry.GetRenderBounds(Avalonia.Media.Pen)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Boolean Avalonia.Media.Geometry.StrokeContains(Avalonia.Media.Pen, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract. @@ -34,4 +36,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. MembersMustExist : Member 'public Avalonia.Utilities.IRef Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract. -Total Issues: 35 +Total Issues: 37 diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Visuals/Media/Geometry.cs index 33aafb58fc..ccef6665a9 100644 --- a/src/Avalonia.Visuals/Media/Geometry.cs +++ b/src/Avalonia.Visuals/Media/Geometry.cs @@ -84,7 +84,7 @@ namespace Avalonia.Media /// /// The stroke thickness. /// The bounding rectangle. - public Rect GetRenderBounds(Pen pen) => PlatformImpl?.GetRenderBounds(pen) ?? Rect.Empty; + public Rect GetRenderBounds(IPen pen) => PlatformImpl?.GetRenderBounds(pen) ?? Rect.Empty; /// /// Indicates whether the geometry's fill contains the specified point. @@ -102,7 +102,7 @@ namespace Avalonia.Media /// The pen to use. /// The point. /// true if the geometry contains the point; otherwise, false. - public bool StrokeContains(Pen pen, Point point) + public bool StrokeContains(IPen pen, Point point) { return PlatformImpl?.StrokeContains(pen, point) == true; } diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs index 4df3aa8ae2..b5052550e6 100644 --- a/src/Avalonia.Visuals/Media/GeometryDrawing.cs +++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs @@ -1,9 +1,13 @@ -using Avalonia.Metadata; +using Avalonia.Media.Immutable; +using Avalonia.Metadata; namespace Avalonia.Media { public class GeometryDrawing : Drawing { + // Adding the Pen's stroke thickness here could yield wrong results due to transforms. + private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0); + public static readonly StyledProperty GeometryProperty = AvaloniaProperty.Register(nameof(Geometry)); @@ -42,9 +46,7 @@ namespace Avalonia.Media public override Rect GetBounds() { - // adding the Pen's stroke thickness here could yield wrong results due to transforms - var pen = new Pen(Brushes.Black, 0); - return Geometry?.GetRenderBounds(pen) ?? new Rect(); + return Geometry?.GetRenderBounds(s_boundsPen) ?? new Rect(); } } } From 13d2c286fdd6aef41624a5fffa4f0dcbbb85f7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 6 Oct 2020 12:22:41 +0200 Subject: [PATCH 086/446] Add doc comments and cleanup code a bit. --- src/Avalonia.Visuals/Media/Drawing.cs | 14 +++++-- src/Avalonia.Visuals/Media/GeometryDrawing.cs | 38 +++++++++++++++---- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Drawing.cs b/src/Avalonia.Visuals/Media/Drawing.cs index 6bc808e407..66d05e7e48 100644 --- a/src/Avalonia.Visuals/Media/Drawing.cs +++ b/src/Avalonia.Visuals/Media/Drawing.cs @@ -1,11 +1,19 @@ -using Avalonia.Platform; - -namespace Avalonia.Media +namespace Avalonia.Media { + /// + /// Abstract class that describes a 2-D drawing. + /// public abstract class Drawing : AvaloniaObject { + /// + /// Draws this drawing to the given . + /// + /// The drawing context. public abstract void Draw(DrawingContext context); + /// + /// Gets the drawing's bounding rectangle. + /// public abstract Rect GetBounds(); } } diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs index b5052550e6..e46e3a7d5f 100644 --- a/src/Avalonia.Visuals/Media/GeometryDrawing.cs +++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs @@ -3,14 +3,36 @@ using Avalonia.Metadata; namespace Avalonia.Media { + /// + /// Represents a drawing operation that combines + /// a geometry with and brush and/or pen to produce rendered content. + /// public class GeometryDrawing : Drawing { // Adding the Pen's stroke thickness here could yield wrong results due to transforms. private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0); - + + /// + /// Defines the property. + /// public static readonly StyledProperty GeometryProperty = AvaloniaProperty.Register(nameof(Geometry)); + /// + /// Defines the property. + /// + public static readonly StyledProperty BrushProperty = + AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent); + + /// + /// Defines the property. + /// + public static readonly StyledProperty PenProperty = + AvaloniaProperty.Register(nameof(Pen)); + + /// + /// Gets or sets the that describes the shape of this . + /// [Content] public Geometry Geometry { @@ -18,18 +40,18 @@ namespace Avalonia.Media set => SetValue(GeometryProperty, value); } - public static readonly StyledProperty BrushProperty = - AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent); - + /// + /// Gets or sets the used to fill the interior of the shape described by this . + /// public IBrush Brush { get => GetValue(BrushProperty); set => SetValue(BrushProperty, value); } - public static readonly StyledProperty PenProperty = - AvaloniaProperty.Register(nameof(Pen)); - + /// + /// Gets or sets the used to stroke this . + /// public IPen Pen { get => GetValue(PenProperty); @@ -46,7 +68,7 @@ namespace Avalonia.Media public override Rect GetBounds() { - return Geometry?.GetRenderBounds(s_boundsPen) ?? new Rect(); + return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty; } } } From 0e70ce4999b3a7c452026d7e095c9b62f5262766 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 12 Oct 2020 18:27:26 +0100 Subject: [PATCH 087/446] rename CompositionHost to CompositionConnector. --- .../{CompositionHost.cs => CompositionConnector.cs} | 6 +++--- .../Composition/CompositionEglGlPlatformSurface.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/Windows/Avalonia.Win32/Composition/{CompositionHost.cs => CompositionConnector.cs} (97%) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs similarity index 97% rename from src/Windows/Avalonia.Win32/Composition/CompositionHost.cs rename to src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs index c88d239670..1519520d89 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionHost.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs @@ -9,7 +9,7 @@ using WinRT; namespace Avalonia.Win32 { - class CompositionHost + class CompositionConnector { internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE { @@ -39,13 +39,13 @@ namespace Avalonia.Win32 [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController); - public static CompositionHost Instance { get; } = new CompositionHost(); + public static CompositionConnector Instance { get; } = new CompositionConnector(); private Compositor _compositor; private Windows.System.DispatcherQueueController _dispatcherQueueController; private CompositionGraphicsDevice _graphicsDevice; - private CompositionHost() + private CompositionConnector() { var glPlatform = AvaloniaLocator.Current.GetService(); diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index 547e2d3c0e..6b6e272580 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -25,7 +25,7 @@ namespace Avalonia.Win32 { using (_egl.PrimaryContext.MakeCurrent()) { - _surfaceInterop = CompositionHost.Instance.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); + _surfaceInterop = CompositionConnector.Instance.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); return blurHost; } } From ae876bd058675e644924eb24d20837f4191208ad Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 12 Oct 2020 18:48:12 +0100 Subject: [PATCH 088/446] simplify composition connection. --- .../Composition/CompositionConnector.cs | 15 ++------------- .../CompositionEglGlPlatformSurface.cs | 4 ++-- src/Windows/Avalonia.Win32/Win32GlManager.cs | 18 ++++++++++++++---- src/Windows/Avalonia.Win32/Win32Platform.cs | 3 ++- src/Windows/Avalonia.Win32/WindowImpl.cs | 9 +++++---- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs index 1519520d89..447f8a8c90 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs @@ -39,24 +39,13 @@ namespace Avalonia.Win32 [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController); - public static CompositionConnector Instance { get; } = new CompositionConnector(); - private Compositor _compositor; private Windows.System.DispatcherQueueController _dispatcherQueueController; private CompositionGraphicsDevice _graphicsDevice; - private CompositionConnector() + internal CompositionConnector(EglPlatformOpenGlInterface egl) { - var glPlatform = AvaloniaLocator.Current.GetService(); - - if (glPlatform is EglPlatformOpenGlInterface egl) - { - Initialize(egl); - } - else - { - throw new PlatformNotSupportedException(); - } + Initialize(egl); } private void Initialize(EglPlatformOpenGlInterface egl) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs index 6b6e272580..441da93787 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs @@ -21,11 +21,11 @@ namespace Avalonia.Win32 _info = info; } - public IBlurHost AttachToCompositionTree(IntPtr hwnd) + public IBlurHost AttachToCompositionTree(CompositionConnector connector, IntPtr hwnd) { using (_egl.PrimaryContext.MakeCurrent()) { - _surfaceInterop = CompositionConnector.Instance.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); + _surfaceInterop = connector.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); return blurHost; } } diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index 523a059e0e..6a6ce2ec3c 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -7,8 +7,6 @@ namespace Avalonia.Win32 { static class Win32GlManager { - private static bool s_attemptedToInitialize; - public static void Initialize() { AvaloniaLocator.CurrentMutable.Bind().ToLazy(() => @@ -19,9 +17,21 @@ namespace Avalonia.Win32 var wgl = WglPlatformOpenGlInterface.TryCreate(); return wgl; } - + if (opts?.AllowEglInitialization == true) - return EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); + { + var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); + + if (egl is { } && + opts?.UseWindowsUIComposition == true && + Win32Platform.WindowsVersion.Major >= 10 && + Win32Platform.WindowsVersion.Build >= 16299) + { + AvaloniaLocator.CurrentMutable.BindToSelf(new CompositionConnector(egl)); + } + + return egl; + } return null; }); diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 493ae45656..5b16cae26e 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -12,6 +12,7 @@ using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.OpenGL; +using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; @@ -113,7 +114,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(new WindowsMountedVolumeInfoProvider()); Win32GlManager.Initialize(); - + _uiThread = Thread.CurrentThread; if (OleContext.Current != null) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 1fd8752a8f..715c8fc01d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -107,9 +107,10 @@ namespace Avalonia.Win32 var glPlatform = AvaloniaLocator.Current.GetService(); - _isUsingComposition = Win32Platform.Options.UseWindowsUIComposition && - Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && - glPlatform != null && glPlatform is EglPlatformOpenGlInterface egl && + var compositionConnector = AvaloniaLocator.Current.GetService(); + + _isUsingComposition = compositionConnector is { } && + glPlatform is EglPlatformOpenGlInterface egl && egl.Display is AngleWin32EglDisplay angleDisplay && angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; @@ -121,7 +122,7 @@ namespace Avalonia.Win32 if (_isUsingComposition) { var cgl = new CompositionEglGlPlatformSurface(glPlatform as EglPlatformOpenGlInterface, this); - _blurHost = cgl.AttachToCompositionTree(_hwnd); + _blurHost = cgl.AttachToCompositionTree(compositionConnector, _hwnd); _gl = cgl; From 614c0c6726eaaf7fbc0a825618cf518cd4320f3e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 12 Oct 2020 18:51:00 +0100 Subject: [PATCH 089/446] tidy composition connector. --- .../Composition/CompositionConnector.cs | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs index 447f8a8c90..3929dc07bf 100644 --- a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs +++ b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs @@ -9,8 +9,12 @@ using WinRT; namespace Avalonia.Win32 { - class CompositionConnector + internal class CompositionConnector { + private Compositor _compositor; + private Windows.System.DispatcherQueueController _dispatcherQueueController; + private CompositionGraphicsDevice _graphicsDevice; + internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE { DQTAT_COM_NONE = 0, @@ -39,23 +43,15 @@ namespace Avalonia.Win32 [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController); - private Compositor _compositor; - private Windows.System.DispatcherQueueController _dispatcherQueueController; - private CompositionGraphicsDevice _graphicsDevice; - - internal CompositionConnector(EglPlatformOpenGlInterface egl) - { - Initialize(egl); - } - - private void Initialize(EglPlatformOpenGlInterface egl) + public CompositionConnector(EglPlatformOpenGlInterface egl) { EnsureDispatcherQueue(); + if (_dispatcherQueueController != null) _compositor = new Compositor(); var interop = _compositor.As(); - + var display = egl.Display as AngleWin32EglDisplay; _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); @@ -78,7 +74,7 @@ namespace Avalonia.Win32 visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1); var container = _compositor.CreateContainerVisual(); - + target.Root = container; var blur = CreateBlur(); @@ -96,9 +92,9 @@ namespace Avalonia.Win32 return surfaceInterop; } - public SpriteVisual CreateBlur() + private SpriteVisual CreateBlur() { - var blurEffect = new GaussianBlurEffect(new CompositionEffectSourceParameter ("backdrop")); + var blurEffect = new GaussianBlurEffect(new CompositionEffectSourceParameter("backdrop")); var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect); var blurBrush = blurEffectFactory.CreateBrush(); @@ -121,7 +117,7 @@ namespace Avalonia.Win32 return visual; } - CompositionTarget CreateDesktopWindowTarget(IntPtr window) + private CompositionTarget CreateDesktopWindowTarget(IntPtr window) { var interop = _compositor.As(); @@ -129,7 +125,7 @@ namespace Avalonia.Win32 return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); } - void EnsureDispatcherQueue() + private void EnsureDispatcherQueue() { if (_dispatcherQueueController == null) { From c1bcdb95860739f4b5dd428d935ca7e57fbde8e3 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 14 Oct 2020 22:19:17 +0200 Subject: [PATCH 090/446] Allow for setting wm shadow hint on a context menu. --- src/Avalonia.Controls/ContextMenu.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index c4df5c1815..ec9649bc64 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -62,6 +62,12 @@ namespace Avalonia.Controls public static readonly StyledProperty PlacementRectProperty = AvaloniaProperty.Register(nameof(PlacementRect)); + /// + /// Defines the property. + /// + public static readonly StyledProperty WindowManagerAddShadowHintProperty = + Popup.WindowManagerAddShadowHintProperty.AddOwner(); + /// /// Defines the property. /// @@ -158,6 +164,12 @@ namespace Avalonia.Controls set { SetValue(PlacementModeProperty, value); } } + public bool WindowManagerAddShadowHint + { + get { return GetValue(WindowManagerAddShadowHintProperty); } + set { SetValue(WindowManagerAddShadowHintProperty, value); } + } + /// /// Gets or sets the the anchor rectangle within the parent that the context menu will be placed /// relative to when is . @@ -267,6 +279,7 @@ namespace Avalonia.Controls PlacementTarget = PlacementTarget ?? control, IsLightDismissEnabled = true, OverlayDismissEventPassThrough = true, + WindowManagerAddShadowHint = WindowManagerAddShadowHint, }; _popup.Opened += PopupOpened; From fb1c7fdcbe683dced07ba90e81fbfd3475f653ab Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 14:13:47 +0300 Subject: [PATCH 091/446] add failing test for issue #4875 --- .../Selection/InternalSelectionModelTests.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs index b64812e290..bb893bd35e 100644 --- a/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs @@ -205,6 +205,33 @@ namespace Avalonia.Controls.UnitTests.Selection Assert.Equal(2, target.SelectedIndex); } + [Fact] + public void Raises_Selection_Changed_On_Items_Reset() + { + var items = new ResettingCollection(new[] { "foo", "bar", "baz" }); + var target = CreateTarget(source: items); + + target.SelectedIndex = 1; + + var changed = new List(); + + target.PropertyChanged += (s, e) => changed.Add(e.PropertyName); + + var oldSelectedIndex = target.SelectedIndex; + var oldSelectedItem = target.SelectedItem; + + items.Reset(new string[0]); + + Assert.NotEqual(oldSelectedIndex, target.SelectedIndex); + Assert.NotEqual(oldSelectedItem, target.SelectedItem); + + Assert.Equal(-1, target.SelectedIndex); + Assert.Equal(null, target.SelectedItem); + + Assert.Contains(nameof(target.SelectedIndex), changed); + Assert.Contains(nameof(target.SelectedItem), changed); + } + [Fact] public void Preserves_Selection_On_Source_Changed() { @@ -222,7 +249,7 @@ namespace Avalonia.Controls.UnitTests.Selection bool nullSource = false) { source ??= !nullSource ? new[] { "foo", "bar", "baz" } : null; - + var result = new InternalSelectionModel { SingleSelect = singleSelect, From 82d17aa8ae98160dca63a021d2cd3b675341d489 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 14:19:06 +0300 Subject: [PATCH 092/446] fix selected item notify after been changed issue #4875 --- src/Avalonia.Controls/Selection/SelectionModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Selection/SelectionModel.cs b/src/Avalonia.Controls/Selection/SelectionModel.cs index 3b5d57a7b8..054974e4f1 100644 --- a/src/Avalonia.Controls/Selection/SelectionModel.cs +++ b/src/Avalonia.Controls/Selection/SelectionModel.cs @@ -442,7 +442,8 @@ namespace Avalonia.Controls.Selection RaisePropertyChanged(nameof(SelectedIndex)); } - if (e.Action == NotifyCollectionChangedAction.Remove && e.OldStartingIndex <= oldSelectedIndex) + if ((e.Action == NotifyCollectionChangedAction.Remove && e.OldStartingIndex <= oldSelectedIndex) || + e.Action == NotifyCollectionChangedAction.Reset) { RaisePropertyChanged(nameof(SelectedItem)); } From 21fadbedd6734fd74e07fdeb7c2383b546ce6b38 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 12:43:17 +0300 Subject: [PATCH 093/446] add tests for text issue:4866 --- .../Controls/TextBlockTests.cs | 54 ++++++++++++++++++ ...estrictedHeight_VerticalAlign.expected.png | Bin 0 -> 527 bytes ...estrictedHeight_VerticalAlign.expected.png | Bin 0 -> 527 bytes 3 files changed, 54 insertions(+) create mode 100644 tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png create mode 100644 tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png diff --git a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs index ab769e4ff0..53a2959848 100644 --- a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs +++ b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs @@ -40,5 +40,59 @@ namespace Avalonia.Direct2D1.RenderTests.Controls await RenderToFile(target); CompareImages(); } + + + [Win32Fact("Has text")] + public async Task RestrictedHeight_VerticalAlign() + { + IControl text(VerticalAlignment verticalAlingnment, bool clip = true, bool restrictHeight = true) + { + return new Border() + { + BorderBrush = Brushes.Blue, + BorderThickness = new Thickness(1), + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + Height = restrictHeight ? 20 : double.NaN, + Margin = new Thickness(1), + Child = new TextBlock + { + FontFamily = new FontFamily("Courier New"), + Background = Brushes.Red, + FontSize = 24, + Foreground = Brushes.Black, + Text = "L", + VerticalAlignment = verticalAlingnment, + ClipToBounds = clip + } + }; + } + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 180, + Height = 80, + + Child = new StackPanel() + { + Orientation = Orientation.Horizontal, + Children = + { + text(VerticalAlignment.Stretch, restrictHeight: false), + text(VerticalAlignment.Center), + text(VerticalAlignment.Stretch), + text(VerticalAlignment.Top), + text(VerticalAlignment.Bottom), + text(VerticalAlignment.Center, clip:false), + text(VerticalAlignment.Stretch, clip:false), + text(VerticalAlignment.Top, clip:false), + text(VerticalAlignment.Bottom, clip:false), + } + } + }; + + await RenderToFile(target); + CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a0a14e52616d76ebd7926248e785e0c6b42296 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^TYxx#gAGV-kceLfq*#ibJVQ8upoSx*gMoqZn5T}U;+IFz$-)-2Qso+}Sug{{q>EZbaGG{*& z^BrhqNHVFa5_;F;p$aq=4P+N;Sm#Ql+4(;Fe8A?um%ofx_5WVyv^uebmhOAP3E}(H zCG9Noq|YTwv?afjs6ErKopYshQv6nS-E$W{D;@W4TQ~J+@8*An?jwXI=Mg#)U&G&X+8f+7-6YKb)<8(jSY;J4LA* zLS7lie%u!OY5#?jJ6KrO-q@BLY*L~g?)mTQydzD%YtOHe-W9g&{YH-6{+~*gZH#}j z(QdKPmrGYpeTj~h269&aYirvscK^brXV0%Z-@4hfWUbTo6SKC?Njp1RW&h{GZ);p@ z!i5)L7|fua=M~7lo_83w~vya^yjSMfh`j0J+JDUAFAD)x=QqT z_~xVcen?B5^9CAdVlq2uPwsR5y=Y<0z|in+9^)@(Q)>rl$*mwsPgg&ebxsLQ0Q>~$ ATmS$7 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..c5a0a14e52616d76ebd7926248e785e0c6b42296 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^TYxx#gAGV-kceLfq*#ibJVQ8upoSx*gMoqZn5T}U;+IFz$-)-2Qso+}Sug{{q>EZbaGG{*& z^BrhqNHVFa5_;F;p$aq=4P+N;Sm#Ql+4(;Fe8A?um%ofx_5WVyv^uebmhOAP3E}(H zCG9Noq|YTwv?afjs6ErKopYshQv6nS-E$W{D;@W4TQ~J+@8*An?jwXI=Mg#)U&G&X+8f+7-6YKb)<8(jSY;J4LA* zLS7lie%u!OY5#?jJ6KrO-q@BLY*L~g?)mTQydzD%YtOHe-W9g&{YH-6{+~*gZH#}j z(QdKPmrGYpeTj~h269&aYirvscK^brXV0%Z-@4hfWUbTo6SKC?Njp1RW&h{GZ);p@ z!i5)L7|fua=M~7lo_83w~vya^yjSMfh`j0J+JDUAFAD)x=QqT z_~xVcen?B5^9CAdVlq2uPwsR5y=Y<0z|in+9^)@(Q)>rl$*mwsPgg&ebxsLQ0Q>~$ ATmS$7 literal 0 HcmV?d00001 From dda966676c62932cc3d92a53a0430d03cb3296eb Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 15:38:43 +0300 Subject: [PATCH 094/446] fix Textblock text alignment with restricted height #4866 --- src/Avalonia.Controls/TextBlock.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index d61519e697..31517ba59d 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -3,6 +3,7 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; +using Avalonia.Layout; namespace Avalonia.Controls { @@ -427,14 +428,32 @@ namespace Avalonia.Controls case TextAlignment.Center: offsetX = (width - TextLayout.Size.Width) / 2; break; + case TextAlignment.Right: - offsetX = width - TextLayout.Size.Width; + offsetX = width - TextLayout.Size.Width; break; } var padding = Padding; - using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top))) + var top = padding.Top; + var textSize = TextLayout.Size; + + if (Bounds.Height < textSize.Height) + { + switch (VerticalAlignment) + { + case VerticalAlignment.Center: + top += (Bounds.Height - textSize.Height) / 2; + break; + + case VerticalAlignment.Bottom: + top += (Bounds.Height - textSize.Height); + break; + } + } + + using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, top))) { TextLayout.Draw(context); } From 1dc2b0b12a12542a95bfc40373cf28d44e8fc688 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 16 Oct 2020 15:39:06 +0300 Subject: [PATCH 095/446] fix TextPresenter text alignment with restricted height #4866 --- .../Presenters/TextPresenter.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 6a6d37605d..078d8050bf 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -4,6 +4,7 @@ using Avalonia.Media; using Avalonia.Metadata; using Avalonia.Threading; using Avalonia.VisualTree; +using Avalonia.Layout; namespace Avalonia.Controls.Presenters { @@ -312,7 +313,24 @@ namespace Avalonia.Controls.Presenters context.FillRectangle(background, new Rect(Bounds.Size)); } - context.DrawText(Foreground, new Point(), FormattedText); + double top = 0; + var textSize = FormattedText.Bounds.Size; + + if (Bounds.Height < textSize.Height) + { + switch (VerticalAlignment) + { + case VerticalAlignment.Center: + top += (Bounds.Height - textSize.Height) / 2; + break; + + case VerticalAlignment.Bottom: + top += (Bounds.Height - textSize.Height); + break; + } + } + + context.DrawText(Foreground, new Point(0, top), FormattedText); } public override void Render(DrawingContext context) From 640bddf5cabf5f6a8bf4cb07fefe341549c62c03 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 14:59:29 +0200 Subject: [PATCH 096/446] Add interface ICommandSource --- src/Avalonia.Input/ICommandSource.cs | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Avalonia.Input/ICommandSource.cs diff --git a/src/Avalonia.Input/ICommandSource.cs b/src/Avalonia.Input/ICommandSource.cs new file mode 100644 index 0000000000..b0dd75e12e --- /dev/null +++ b/src/Avalonia.Input/ICommandSource.cs @@ -0,0 +1,31 @@ +using System.Windows.Input; + +namespace Avalonia.Input +{ + /// + /// An interface for classes that know how to invoke a Command. + /// + public interface ICommandSource + { + /// + /// The command that will be executed when the class is "invoked." + /// Classes that implement this interface should enable or disable based on the command's CanExecute return value. + /// The property may be implemented as read-write if desired. + /// + ICommand Command { get; } + + /// + /// The parameter that will be passed to the command when executing the command. + /// The property may be implemented as read-write if desired. + /// + object CommandParameter { get; } + + + /// + /// Bor the bheavior CanExecuteChanged + /// + /// + /// + void CanExecuteChanged(object sender, System.EventArgs e); + } +} From ef385675470f347aee079f3c9517f88014aa3789 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 15:48:10 +0200 Subject: [PATCH 097/446] Implemented ICommandSource --- src/Avalonia.Controls/Button.cs | 4 +++- src/Avalonia.Controls/MenuItem.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index e94d00b2ff..91eef3947b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -30,7 +30,7 @@ namespace Avalonia.Controls /// A button control. /// [PseudoClasses(":pressed")] - public class Button : ContentControl + public class Button : ContentControl, ICommandSource { /// /// Defines the property. @@ -492,5 +492,7 @@ namespace Avalonia.Controls { PseudoClasses.Set(":pressed", isPressed); } + + void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); } } diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 7d4fef009d..d034f54df2 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls /// A menu item control. /// [PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")] - public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable + public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource { /// /// Defines the property. @@ -609,6 +609,8 @@ namespace Avalonia.Controls SelectedItem = null; } + void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); + /// /// A dependency resolver which returns a . /// From fa1476a6622101c383a19e499eb44ad352306e14 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 16:05:59 +0200 Subject: [PATCH 098/446] Fix Hotkey CommandParameter --- src/Avalonia.Controls/HotkeyManager.cs | 17 +++++++++++------ src/Avalonia.Controls/NativeMenuItem.cs | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index 95752e7875..b104b7a9a8 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Input; using Avalonia.Controls.Utils; +using Avalonia.Data.Core; using Avalonia.Input; namespace Avalonia.Controls @@ -12,14 +13,14 @@ namespace Avalonia.Controls class HotkeyCommandWrapper : ICommand { - public HotkeyCommandWrapper(IControl control) + public HotkeyCommandWrapper(ICommandSource control) { - Control = control; + CommandSource = control; } - public readonly IControl Control; + public readonly ICommandSource CommandSource; - private ICommand GetCommand() => Control.GetValue(Button.CommandProperty); + private ICommand GetCommand() => CommandSource.Command; public bool CanExecute(object parameter) => GetCommand()?.CanExecute(parameter) ?? false; @@ -37,6 +38,7 @@ namespace Avalonia.Controls private TopLevel _root; private IDisposable _parentSub; private IDisposable _hotkeySub; + private IDisposable _commandParameterChangedSubscriber; private KeyGesture _hotkey; private readonly HotkeyCommandWrapper _wrapper; private KeyBinding _binding; @@ -44,7 +46,7 @@ namespace Avalonia.Controls public Manager(IControl control) { _control = control; - _wrapper = new HotkeyCommandWrapper(_control); + _wrapper = new HotkeyCommandWrapper(_control as ICommandSource); } public void Init() @@ -77,6 +79,7 @@ namespace Avalonia.Controls { if (_root != null && _binding != null) _root.KeyBindings.Remove(_binding); + _commandParameterChangedSubscriber?.Dispose(); _binding = null; } @@ -85,6 +88,8 @@ namespace Avalonia.Controls if (_root != null && _hotkey != null) { _binding = new KeyBinding() {Gesture = _hotkey, Command = _wrapper}; + _commandParameterChangedSubscriber = _binding.Bind(KeyBinding.CommandParameterProperty + , ExpressionObserver.Create(((ICommandSource)_control), o => o.CommandParameter)); _root.KeyBindings.Add(_binding); } } @@ -102,7 +107,7 @@ namespace Avalonia.Controls HotKeyProperty.Changed.Subscribe(args => { var control = args.Sender as IControl; - if (args.OldValue != null|| control == null) + if (args.OldValue != null || control == null || !(control is ICommandSource)) return; new Manager(control).Init(); }); diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs index a0fec9e677..d1adda3b0d 100644 --- a/src/Avalonia.Controls/NativeMenuItem.cs +++ b/src/Avalonia.Controls/NativeMenuItem.cs @@ -148,7 +148,7 @@ namespace Avalonia.Controls void CanExecuteChanged() { - IsEnabled = _command?.CanExecute(null) ?? true; + IsEnabled = _command?.CanExecute(CommandParameter) ?? true; } public bool HasClickHandlers => Clicked != null; From 9ffd78a3baa31772de40dd3aad018451486927e7 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 16:29:51 +0200 Subject: [PATCH 099/446] Add Test --- .../Utils/HotKeyManagerTests.cs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs index 9f2712c93c..a29eb0f0c7 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs @@ -5,6 +5,9 @@ using Avalonia.Input; using Avalonia.Platform; using Avalonia.Styling; using Xunit; +using System; +using Avalonia.Input.Raw; +using Factory = System.Func, Avalonia.Controls.Window, Avalonia.AvaloniaObject>; namespace Avalonia.Controls.UnitTests.Utils { @@ -54,6 +57,84 @@ namespace Avalonia.Controls.UnitTests.Utils } } + [Theory] + [MemberData(nameof(ElementsFactory))] + public void HotKeyManager_Should_Use_CommandParameter(string factoryName, Factory factory) + { + using (AvaloniaLocator.EnterScope()) + { + var styler = new Mock(); + var target = new KeyboardDevice(); + var commandResult = 0; + var expectedParameter = 1; + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(new WindowingPlatformMock()) + .Bind().ToConstant(styler.Object); + + var gesture = new KeyGesture(Key.A, KeyModifiers.Control); + + var action = new Action(parameter => + { + if (parameter is int value) + { + commandResult = value; + } + }); + + var root = new Window(); + var element = factory(expectedParameter, action, root); + + root.Template = CreateWindowTemplate(); + root.ApplyTemplate(); + root.Presenter.ApplyTemplate(); + + HotKeyManager.SetHotKey(element, gesture); + + target.ProcessRawEvent(new RawKeyEventArgs(target, + 0, + root, + RawKeyEventType.KeyDown, + Key.A, + RawInputModifiers.Control)); + + Assert.True(expectedParameter == commandResult, $"{factoryName} HotKey did not carry the CommandParameter."); + } + } + + public static TheoryData ElementsFactory => + new TheoryData() + { + {nameof(Button), MakeButton}, + {nameof(MenuItem),MakeMenu}, + }; + + private static AvaloniaObject MakeMenu(int expectedParameter, Action action, Window root) + { + var menuitem = new MenuItem() + { + Command = new Command(action), + CommandParameter = expectedParameter, + }; + var rootMenu = new Menu(); + + rootMenu.Items = new[] { menuitem }; + + root.Content = rootMenu; + return menuitem; + } + + private static AvaloniaObject MakeButton(int expectedParameter, Action action, Window root) + { + var button = new Button() + { + Command = new Command(action), + CommandParameter = expectedParameter, + }; + + root.Content = button; + return button; + } + private FuncControlTemplate CreateWindowTemplate() { return new FuncControlTemplate((parent, scope) => @@ -65,5 +146,22 @@ namespace Avalonia.Controls.UnitTests.Utils }.RegisterInNameScope(scope); }); } + + class Command : System.Windows.Input.ICommand + { + private readonly Action _execeute; + +#pragma warning disable 67 // Event not used + public event EventHandler CanExecuteChanged; +#pragma warning restore 67 // Event not used + + public Command(Action execeute) + { + _execeute = execeute; + } + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) => _execeute?.Invoke(parameter); + } } } From f1f2df8dd48d42ddc7cc669235988b38cf24b259 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 25 Sep 2020 17:32:26 +0200 Subject: [PATCH 100/446] Fixes Issue #4734 : HotKeys trigger command when control is disabled --- src/Avalonia.Controls/HotkeyManager.cs | 2 +- src/Avalonia.Input/ICommandSource.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index b104b7a9a8..f210ecfdaf 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls private ICommand GetCommand() => CommandSource.Command; - public bool CanExecute(object parameter) => GetCommand()?.CanExecute(parameter) ?? false; + public bool CanExecute(object parameter) => CommandSource.IsEffectivelyEnabled; public void Execute(object parameter) => GetCommand()?.Execute(parameter); diff --git a/src/Avalonia.Input/ICommandSource.cs b/src/Avalonia.Input/ICommandSource.cs index b0dd75e12e..ba2e8eed4e 100644 --- a/src/Avalonia.Input/ICommandSource.cs +++ b/src/Avalonia.Input/ICommandSource.cs @@ -27,5 +27,10 @@ namespace Avalonia.Input /// /// void CanExecuteChanged(object sender, System.EventArgs e); + + /// + /// Gets a value indicating whether this control and all its parents are enabled. + /// + bool IsEffectivelyEnabled { get; } } } From f0efe9aef0d2e4953f5ed034e21359c95dcc4499 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 25 Sep 2020 17:33:13 +0200 Subject: [PATCH 101/446] Add test Issue #4734 --- .../Utils/HotKeyManagerTests.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs index a29eb0f0c7..151c3070f4 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs @@ -101,6 +101,49 @@ namespace Avalonia.Controls.UnitTests.Utils } } + + [Theory] + [MemberData(nameof(ElementsFactory))] + public void HotKeyManager_Should_Do_Not_Executed_When_IsEnabled_False(string factoryName, Factory factory) + { + using (AvaloniaLocator.EnterScope()) + { + var styler = new Mock(); + var target = new KeyboardDevice(); + var isExecuted = false; + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(new WindowingPlatformMock()) + .Bind().ToConstant(styler.Object); + + var gesture = new KeyGesture(Key.A, KeyModifiers.Control); + + var action = new Action(parameter => + { + isExecuted = true; + }); + + var root = new Window(); + var element = factory(0, action, root) as InputElement; + + element.IsEnabled = false; + + root.Template = CreateWindowTemplate(); + root.ApplyTemplate(); + root.Presenter.ApplyTemplate(); + + HotKeyManager.SetHotKey(element, gesture); + + target.ProcessRawEvent(new RawKeyEventArgs(target, + 0, + root, + RawKeyEventType.KeyDown, + Key.A, + RawInputModifiers.Control)); + + Assert.True(isExecuted == false, $"{factoryName} Execution raised when IsEnabled is false."); + } + } + public static TheoryData ElementsFactory => new TheoryData() { From c7a21d7268aad472e9022777b626120cdc6b29e3 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 9 Oct 2020 09:51:54 +0200 Subject: [PATCH 102/446] Removed CommandParameter Binding --- src/Avalonia.Controls/HotkeyManager.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index f210ecfdaf..dabc35dd24 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -1,7 +1,6 @@ using System; using System.Windows.Input; using Avalonia.Controls.Utils; -using Avalonia.Data.Core; using Avalonia.Input; namespace Avalonia.Controls @@ -22,9 +21,12 @@ namespace Avalonia.Controls private ICommand GetCommand() => CommandSource.Command; - public bool CanExecute(object parameter) => CommandSource.IsEffectivelyEnabled; + public bool CanExecute(object parameter) => + CommandSource.Command?.CanExecute(CommandSource.CommandParameter) == true + && CommandSource.IsEffectivelyEnabled; - public void Execute(object parameter) => GetCommand()?.Execute(parameter); + public void Execute(object parameter) => + GetCommand()?.Execute(CommandSource.CommandParameter); #pragma warning disable 67 // Event not used public event EventHandler CanExecuteChanged; @@ -38,7 +40,6 @@ namespace Avalonia.Controls private TopLevel _root; private IDisposable _parentSub; private IDisposable _hotkeySub; - private IDisposable _commandParameterChangedSubscriber; private KeyGesture _hotkey; private readonly HotkeyCommandWrapper _wrapper; private KeyBinding _binding; @@ -79,7 +80,6 @@ namespace Avalonia.Controls { if (_root != null && _binding != null) _root.KeyBindings.Remove(_binding); - _commandParameterChangedSubscriber?.Dispose(); _binding = null; } @@ -87,9 +87,7 @@ namespace Avalonia.Controls { if (_root != null && _hotkey != null) { - _binding = new KeyBinding() {Gesture = _hotkey, Command = _wrapper}; - _commandParameterChangedSubscriber = _binding.Bind(KeyBinding.CommandParameterProperty - , ExpressionObserver.Create(((ICommandSource)_control), o => o.CommandParameter)); + _binding = new KeyBinding() { Gesture = _hotkey, Command = _wrapper }; _root.KeyBindings.Add(_binding); } } From b8728c8f8ccf08a95f68114d0c3f2f00bb7ff0d3 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 9 Oct 2020 09:53:54 +0200 Subject: [PATCH 103/446] Add warning when HotKey is attached to a control that does not support ICommandSource. --- src/Avalonia.Controls/HotkeyManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index dabc35dd24..baae37e022 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -105,8 +105,13 @@ namespace Avalonia.Controls HotKeyProperty.Changed.Subscribe(args => { var control = args.Sender as IControl; - if (args.OldValue != null || control == null || !(control is ICommandSource)) + if (args.OldValue != null || control == null || !(control is ICommandSource)) + { + Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?. + Log(control, $"The element {args.Sender.GetType().Name} do not support HotKey({args.NewValue})."); return; + } + new Manager(control).Init(); }); } From abff848c8d844eebd32a8a61062a471ccc807db7 Mon Sep 17 00:00:00 2001 From: workgroupengineering Date: Tue, 13 Oct 2020 17:01:37 +0200 Subject: [PATCH 104/446] Update warning message Co-authored-by: Steven Kirk --- src/Avalonia.Controls/HotkeyManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index baae37e022..b76b7fbed5 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -108,7 +108,7 @@ namespace Avalonia.Controls if (args.OldValue != null || control == null || !(control is ICommandSource)) { Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?. - Log(control, $"The element {args.Sender.GetType().Name} do not support HotKey({args.NewValue})."); + Log(control, $"The element {args.Sender.GetType().Name} does not support binding a HotKey ({args.NewValue})."); return; } From 77cd5153329cde4763a55eae30f1718f47c3c535 Mon Sep 17 00:00:00 2001 From: mj Date: Sun, 18 Oct 2020 09:10:33 -0600 Subject: [PATCH 105/446] Fixed returns value; Issue 4883 --- src/Avalonia.Controls/ItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 3aec06e4eb..4dc8aec6f3 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -169,7 +169,7 @@ namespace Avalonia.Controls /// /// The collection. /// The index. - /// The index of the item or -1 if the item was not found. + /// The item at the given index or null if the index is out of bounds. protected static object ElementAt(IEnumerable items, int index) { if (index != -1 && index < items.Count()) From 1aa50361bb1a27821a407a26df8f4216cdad00c4 Mon Sep 17 00:00:00 2001 From: bmello4688 Date: Tue, 20 Oct 2020 08:55:17 -0400 Subject: [PATCH 106/446] add XmlnsPrefix which is in WPF --- .../Metadata/XmlnsPrefixAttribute.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/Avalonia.Base/Metadata/XmlnsPrefixAttribute.cs diff --git a/src/Avalonia.Base/Metadata/XmlnsPrefixAttribute.cs b/src/Avalonia.Base/Metadata/XmlnsPrefixAttribute.cs new file mode 100644 index 0000000000..0f091d540a --- /dev/null +++ b/src/Avalonia.Base/Metadata/XmlnsPrefixAttribute.cs @@ -0,0 +1,30 @@ +using System; + +namespace Avalonia.Metadata +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class XmlnsPrefixAttribute : Attribute + { + /// + /// Constructor + /// + /// XML namespce + /// recommended prefix + public XmlnsPrefixAttribute(string xmlNamespace, string prefix) + { + XmlNamespace = xmlNamespace ?? throw new ArgumentNullException(nameof(xmlNamespace)); + + Prefix = prefix ?? throw new ArgumentNullException(nameof(prefix)); + } + + /// + /// XML Namespace + /// + public string XmlNamespace { get; } + + /// + /// New Xml Namespace + /// + public string Prefix { get; } + } +} From 1497882cf64941e1f1b21957b1e490290637bdb2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 20 Oct 2020 16:54:48 +0300 Subject: [PATCH 107/446] Enable light dismiss overlay for popups --- src/Avalonia.Controls/Primitives/VisualLayerManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs index d8d3450c6f..457a9ea282 100644 --- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs +++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs @@ -67,8 +67,6 @@ namespace Avalonia.Controls.Primitives { get { - if (IsPopup) - return null; var rv = FindLayer(); if (rv == null) { From 32b8cc811c087ddd8cda77668a7babed025b5093 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 20 Oct 2020 15:46:06 +0100 Subject: [PATCH 108/446] pass surfaceorigin to FboSkiaSurface. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 7 ++++++- src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs | 2 +- src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs | 2 ++ src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs | 4 ++-- src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs | 4 ++++ src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs | 4 ++-- src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs | 3 ++- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 2 +- src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs | 6 +++--- 9 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index cbc896813f..dac284ce1a 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -36,6 +36,7 @@ namespace Avalonia.Skia private readonly SKPaint _fillPaint = new SKPaint(); private readonly SKPaint _boxShadowPaint = new SKPaint(); private static SKShader s_acrylicNoiseShader; + private readonly GRSurfaceOrigin _surfaceOrigin; /// /// Context create info. @@ -76,6 +77,8 @@ namespace Avalonia.Skia /// Skia GPU provider context (optional) /// public ISkiaGpu Gpu; + + public ISkiaGpuRenderSession CurrentSession; } /// @@ -96,6 +99,8 @@ namespace Avalonia.Skia Surface = createInfo.Surface; Canvas = createInfo.Canvas ?? createInfo.Surface?.Canvas; + _surfaceOrigin = createInfo.CurrentSession?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft; + if (Canvas == null) { throw new ArgumentException("Invalid create info - no Canvas provided", nameof(createInfo)); @@ -972,7 +977,7 @@ namespace Avalonia.Skia Gpu = _gpu }; - return new SurfaceRenderTarget(createInfo); + return new SurfaceRenderTarget(createInfo, _surfaceOrigin); } /// diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs index 869c261f1b..7442f90ecf 100644 --- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs @@ -21,7 +21,7 @@ namespace Avalonia.Skia /// Creates an offscreen render target surface /// /// size in pixels - ISkiaSurface TryCreateSurface(PixelSize size); + ISkiaSurface TryCreateSurface(PixelSize size, GRSurfaceOrigin surfaceOrigin); } public interface ISkiaSurface : IDisposable diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs index a4e2bfed52..5faa6a41ec 100644 --- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs @@ -22,5 +22,7 @@ namespace Avalonia.Skia /// Scaling factor. /// double ScaleFactor { get; } + + GRSurfaceOrigin SurfaceOrigin { get; } } } diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs index 24464c39d9..8ab275df63 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs @@ -14,7 +14,7 @@ namespace Avalonia.Skia private int _texture; private static readonly bool[] TrueFalse = new[] { true, false }; - public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize) + public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin) { _grContext = grContext; _glContext = glContext; @@ -93,7 +93,7 @@ namespace Avalonia.Skia var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8, new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat())); Surface = SKSurface.Create(_grContext, target, - GRSurfaceOrigin.TopLeft, SKColorType.Rgba8888); + surfaceOrigin, SKColorType.Rgba8888); CanBlit = gl.BlitFramebuffer != null; } diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs index e109922a3f..b4c5619c85 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs @@ -39,6 +39,8 @@ namespace Avalonia.Skia _backendRenderTarget = backendRenderTarget; _surface = surface; _glSession = glSession; + + SurfaceOrigin = glSession.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft; } public void Dispose() { @@ -48,6 +50,8 @@ namespace Avalonia.Skia GrContext.Flush(); _glSession.Dispose(); } + + public GRSurfaceOrigin SurfaceOrigin { get; } public GRContext GrContext { get; } public SKSurface SkSurface => _surface; diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index c02d813e24..02e3766d40 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -47,7 +47,7 @@ namespace Avalonia.Skia return null; } - public ISkiaSurface TryCreateSurface(PixelSize size) + public ISkiaSurface TryCreateSurface(PixelSize size, GRSurfaceOrigin surfaceOrigin) { // Only windows platform needs our FBO trickery if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -62,7 +62,7 @@ namespace Avalonia.Skia return null; try { - var surface = new FboSkiaSurface(_grContext, _glContext, size); + var surface = new FboSkiaSurface(_grContext, _glContext, size, surfaceOrigin); _canCreateSurfaces = true; return surface; } diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs index 9992c9ba8c..6626546c0c 100644 --- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs @@ -33,7 +33,8 @@ namespace Avalonia.Skia Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor, VisualBrushRenderer = visualBrushRenderer, DisableTextLcdRendering = true, - Gpu = _skiaGpu + Gpu = _skiaGpu, + CurrentSession = session }; return new DrawingContextImpl(nfo, session); diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index d6f76a2c20..6048482110 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -127,7 +127,7 @@ namespace Avalonia.Skia DisableTextLcdRendering = false }; - return new SurfaceRenderTarget(createInfo); + return new SurfaceRenderTarget(createInfo, GRSurfaceOrigin.TopLeft); } /// diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index 61b599a731..905c9160a7 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs @@ -42,7 +42,7 @@ namespace Avalonia.Skia /// Create new surface render target. /// /// Create info. - public SurfaceRenderTarget(CreateInfo createInfo) + public SurfaceRenderTarget(CreateInfo createInfo, GRSurfaceOrigin surfaceOrigin) { PixelSize = new PixelSize(createInfo.Width, createInfo.Height); Dpi = createInfo.Dpi; @@ -51,7 +51,7 @@ namespace Avalonia.Skia _grContext = createInfo.GrContext; _gpu = createInfo.Gpu; - _surface = _gpu?.TryCreateSurface(PixelSize); + _surface = _gpu?.TryCreateSurface(PixelSize, surfaceOrigin); if (_surface == null) _surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format)); @@ -100,7 +100,7 @@ namespace Avalonia.Skia VisualBrushRenderer = visualBrushRenderer, DisableTextLcdRendering = _disableLcdRendering, GrContext = _grContext, - Gpu = _gpu + Gpu = _gpu, }; return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++)); From d218e2be2074a39cc0348c90c2cfe7334fdab73b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 20 Oct 2020 20:32:32 +0100 Subject: [PATCH 109/446] dont save surfaceorigin as a field of drawing context. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index dac284ce1a..136b14140e 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -36,7 +36,7 @@ namespace Avalonia.Skia private readonly SKPaint _fillPaint = new SKPaint(); private readonly SKPaint _boxShadowPaint = new SKPaint(); private static SKShader s_acrylicNoiseShader; - private readonly GRSurfaceOrigin _surfaceOrigin; + private readonly ISkiaGpuRenderSession _session; /// /// Context create info. @@ -99,7 +99,7 @@ namespace Avalonia.Skia Surface = createInfo.Surface; Canvas = createInfo.Canvas ?? createInfo.Surface?.Canvas; - _surfaceOrigin = createInfo.CurrentSession?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft; + _session = createInfo.CurrentSession; if (Canvas == null) { @@ -977,7 +977,7 @@ namespace Avalonia.Skia Gpu = _gpu }; - return new SurfaceRenderTarget(createInfo, _surfaceOrigin); + return new SurfaceRenderTarget(createInfo, _session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft); } /// From ce08964f0371452ce8301cd2206d6e32f295982f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 20 Oct 2020 20:34:51 +0100 Subject: [PATCH 110/446] refactor how surface origin is passed. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 5 +++-- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 2 +- src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 136b14140e..98692b7483 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -974,10 +974,11 @@ namespace Avalonia.Skia Format = format, DisableTextLcdRendering = !_canTextUseLcdRendering, GrContext = _grContext, - Gpu = _gpu + Gpu = _gpu, + SurfaceOrigin = _session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft }; - return new SurfaceRenderTarget(createInfo, _session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft); + return new SurfaceRenderTarget(createInfo); } /// diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 6048482110..d6f76a2c20 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -127,7 +127,7 @@ namespace Avalonia.Skia DisableTextLcdRendering = false }; - return new SurfaceRenderTarget(createInfo, GRSurfaceOrigin.TopLeft); + return new SurfaceRenderTarget(createInfo); } /// diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index 905c9160a7..d3d4515689 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs @@ -42,7 +42,7 @@ namespace Avalonia.Skia /// Create new surface render target. /// /// Create info. - public SurfaceRenderTarget(CreateInfo createInfo, GRSurfaceOrigin surfaceOrigin) + public SurfaceRenderTarget(CreateInfo createInfo) { PixelSize = new PixelSize(createInfo.Width, createInfo.Height); Dpi = createInfo.Dpi; @@ -51,7 +51,7 @@ namespace Avalonia.Skia _grContext = createInfo.GrContext; _gpu = createInfo.Gpu; - _surface = _gpu?.TryCreateSurface(PixelSize, surfaceOrigin); + _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.SurfaceOrigin); if (_surface == null) _surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format)); @@ -218,6 +218,8 @@ namespace Avalonia.Skia public GRContext GrContext; public ISkiaGpu Gpu; + + public GRSurfaceOrigin SurfaceOrigin; } } } From e04fe994fb9c032992c07380749933b4f8b6dca8 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 21 Oct 2020 00:06:10 +0300 Subject: [PATCH 111/446] do not make any layout in ItemVirtualizer while not attached to visual tree --- src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index bdc68bee7e..7d50ef7d33 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -512,6 +512,13 @@ namespace Avalonia.Controls.Presenters var generator = Owner.ItemContainerGenerator; var newOffset = -1.0; + //better not trigger any container generation/recycle while or layout stuff + //before panel is attached/visible + if (!panel.IsAttachedToVisualTree) + { + return null; + } + if (!panel.IsMeasureValid && panel.PreviousMeasure.HasValue) { //before any kind of scrolling we need to make sure panel measure is valid From a93ecd1c66d0126409fdfaea5cfc73124fed069d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 12:47:33 +0300 Subject: [PATCH 112/446] [X11] Handle PointerLeave through XI2 --- src/Avalonia.X11/XI2Manager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index 742973e0da..9349824525 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -162,7 +162,9 @@ namespace Avalonia.X11 | XEventMask.Button4MotionMask | XEventMask.Button5MotionMask | XEventMask.ButtonPressMask - | XEventMask.ButtonReleaseMask; + | XEventMask.ButtonReleaseMask + | XEventMask.LeaveWindowMask + | XEventMask.EnterWindowMask; } public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid); @@ -201,6 +203,12 @@ namespace Avalonia.X11 return; } + if (ev.Type == XiEventType.XI_Leave) + { + client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, + RawPointerEventType.LeaveWindow, ev.Position, ev.Modifiers)); + } + if (_multitouch && ev.Emulated) return; From fb7b7a002ce8f46a924ea307a8b6710be1de0670 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 13:04:05 +0300 Subject: [PATCH 113/446] Fixed initial pointerover at (0, 0) when window is just shown --- src/Avalonia.Input/MouseDevice.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index cec5029c18..5c63546f5d 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -19,6 +19,7 @@ namespace Avalonia.Input private readonly Pointer _pointer; private bool _disposed; + private PixelPoint? _position; public MouseDevice(Pointer? pointer = null) { @@ -39,10 +40,11 @@ namespace Avalonia.Input /// /// Gets the mouse position, in screen coordinates. /// + [Obsolete("Use events instead")] public PixelPoint Position { - get; - protected set; + get => _position ?? new PixelPoint(-1, -1); + protected set => _position = value; } /// @@ -91,7 +93,16 @@ namespace Avalonia.Input public void SceneInvalidated(IInputRoot root, Rect rect) { - var clientPoint = root.PointToClient(Position); + // Pointer is outside of the target area + if (_position == null ) + { + if (root.PointerOverElement != null) + ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); + return; + } + + + var clientPoint = root.PointToClient(_position.Value); if (rect.Contains(clientPoint)) { @@ -132,7 +143,7 @@ namespace Avalonia.Input if(mouse._disposed) return; - Position = e.Root.PointToScreen(e.Position); + _position = e.Root.PointToScreen(e.Position); var props = CreateProperties(e); var keyModifiers = KeyModifiersUtils.ConvertToKey(e.InputModifiers); switch (e.Type) @@ -176,6 +187,7 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); + _position = null; ClearPointerOver(this, timestamp, root, properties, inputModifiers); } From aa48b5733e14be820a01c452e15c947144cb81bc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Oct 2020 12:52:23 +0200 Subject: [PATCH 114/446] Replaced LogToDebug with LogToTrace. Calls to `System.Diagnostics.Debug` are removed in release builds meaning nothing was getting logged. Fixes #4901 --- samples/BindingDemo/App.xaml.cs | 2 +- samples/ControlCatalog.Desktop/Program.cs | 2 +- samples/ControlCatalog.NetCore/Program.cs | 2 +- samples/RenderDemo/App.xaml.cs | 2 +- samples/Sandbox/Program.cs | 2 +- samples/VirtualizationDemo/Program.cs | 2 +- src/Avalonia.Base/ApiCompatBaseline.txt | 3 ++- .../{DebugLogSink.cs => TraceLogSink.cs} | 14 +++++++------- src/Avalonia.Controls/LoggingExtensions.cs | 19 +++++++++++++++---- 9 files changed, 30 insertions(+), 18 deletions(-) rename src/Avalonia.Base/Logging/{DebugLogSink.cs => TraceLogSink.cs} (92%) diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs index 5c38ab8305..eb2da03a7e 100644 --- a/samples/BindingDemo/App.xaml.cs +++ b/samples/BindingDemo/App.xaml.cs @@ -26,6 +26,6 @@ namespace BindingDemo => AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); } } diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index e0cc8cc904..5af646b180 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -18,7 +18,7 @@ namespace ControlCatalog /// public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() - .LogToDebug() + .LogToTrace() .UsePlatformDetect() .UseReactiveUI(); diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 142736a0bb..c1fcdd6216 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -121,7 +121,7 @@ namespace ControlCatalog.NetCore .UseSkia() .UseReactiveUI() .UseManagedSystemDialogs() - .LogToDebug(); + .LogToTrace(); static void SilenceConsole() { diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs index 2247fd7c5a..e6ec963d75 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -33,6 +33,6 @@ namespace RenderDemo }) .UsePlatformDetect() .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); } } diff --git a/samples/Sandbox/Program.cs b/samples/Sandbox/Program.cs index 4d7eda8d9f..7d41a8b8c0 100644 --- a/samples/Sandbox/Program.cs +++ b/samples/Sandbox/Program.cs @@ -10,7 +10,7 @@ namespace Sandbox AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug() + .LogToTrace() .StartWithClassicDesktopLifetime(args); } } diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs index c23bfd7ad9..46c05f74b2 100644 --- a/samples/VirtualizationDemo/Program.cs +++ b/samples/VirtualizationDemo/Program.cs @@ -9,7 +9,7 @@ namespace VirtualizationDemo => AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); public static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt index 4668a572c5..5d19373a92 100644 --- a/src/Avalonia.Base/ApiCompatBaseline.txt +++ b/src/Avalonia.Base/ApiCompatBaseline.txt @@ -1,3 +1,4 @@ Compat issues with assembly Avalonia.Base: CannotAddAbstractMembers : Member 'protected System.IObservable Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract. -Total Issues: 1 +TypesMustExist : Type 'Avalonia.Logging.DebugLogSink' does not exist in the implementation but it does exist in the contract. +Total Issues: 2 diff --git a/src/Avalonia.Base/Logging/DebugLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs similarity index 92% rename from src/Avalonia.Base/Logging/DebugLogSink.cs rename to src/Avalonia.Base/Logging/TraceLogSink.cs index 3695afa860..f597844378 100644 --- a/src/Avalonia.Base/Logging/DebugLogSink.cs +++ b/src/Avalonia.Base/Logging/TraceLogSink.cs @@ -6,12 +6,12 @@ using Avalonia.Utilities; namespace Avalonia.Logging { - public class DebugLogSink : ILogSink + public class TraceLogSink : ILogSink { private readonly LogEventLevel _level; private readonly IList _areas; - public DebugLogSink( + public TraceLogSink( LogEventLevel minimumLevel, IList areas = null) { @@ -28,7 +28,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source)); + Trace.WriteLine(Format(area, messageTemplate, source)); } } @@ -36,7 +36,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0)); } } @@ -44,7 +44,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); } } @@ -52,7 +52,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); } } @@ -60,7 +60,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValues)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValues)); } } diff --git a/src/Avalonia.Controls/LoggingExtensions.cs b/src/Avalonia.Controls/LoggingExtensions.cs index 44e570bdfa..9eb3b140f6 100644 --- a/src/Avalonia.Controls/LoggingExtensions.cs +++ b/src/Avalonia.Controls/LoggingExtensions.cs @@ -1,25 +1,36 @@ -using Avalonia.Controls; +using System; +using Avalonia.Controls; using Avalonia.Logging; namespace Avalonia { public static class LoggingExtensions { + [Obsolete("Use LogToTrace")] + public static T LogToDebug( + this T builder, + LogEventLevel level = LogEventLevel.Warning, + params string[] areas) + where T : AppBuilderBase, new() + { + return LogToTrace(builder, level, areas); + } + /// - /// Logs Avalonia events to the sink. + /// Logs Avalonia events to the sink. /// /// The application class type. /// The app builder instance. /// The minimum level to log. /// The areas to log. Valid values are listed in . /// The app builder instance. - public static T LogToDebug( + public static T LogToTrace( this T builder, LogEventLevel level = LogEventLevel.Warning, params string[] areas) where T : AppBuilderBase, new() { - Logger.Sink = new DebugLogSink(level, areas); + Logger.Sink = new TraceLogSink(level, areas); return builder; } } From 4f6dd5a44a9da62446b91ed0f620b645bff00f60 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 14:42:00 +0300 Subject: [PATCH 115/446] [X11] XI2 fix --- src/Avalonia.X11/XI2Manager.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index 9349824525..d0655d05c1 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -13,7 +13,8 @@ namespace Avalonia.X11 { XiEventType.XI_Motion, XiEventType.XI_ButtonPress, - XiEventType.XI_ButtonRelease + XiEventType.XI_ButtonRelease, + XiEventType.XI_Leave }; private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[] @@ -177,9 +178,10 @@ namespace Avalonia.X11 _pointerDevice.Update(changed->Classes, changed->NumClasses); } - + if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion) - || (xev->evtype>=XiEventType.XI_TouchBegin&&xev->evtype<=XiEventType.XI_TouchEnd)) + || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd) + || xev->evtype == XiEventType.XI_Leave) { var dev = (XIDeviceEvent*)xev; if (_clients.TryGetValue(dev->EventWindow, out var client)) @@ -306,7 +308,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - if (ev->buttons.MaskLen > 0) + if (ev->buttons.MaskLen > 1 && Type != XiEventType.XI_Leave) { var buttons = ev->buttons.Mask; if (XIMaskIsSet(buttons, 1)) @@ -324,9 +326,11 @@ namespace Avalonia.X11 Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); var values = ev->valuators.Values; - for (var c = 0; c < ev->valuators.MaskLen * 8; c++) - if (XIMaskIsSet(ev->valuators.Mask, c)) - Valuators[c] = *values++; + if(ev->valuators.Mask != null) + for (var c = 0; c < ev->valuators.MaskLen * 8; c++) + if (XIMaskIsSet(ev->valuators.Mask, c)) + Valuators[c] = *values++; + if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease) Button = ev->detail; Detail = ev->detail; From 5f3d81bbfcd33ad5bca32b188135355dcbde08c3 Mon Sep 17 00:00:00 2001 From: Mikhail Poliudov Date: Wed, 21 Oct 2020 19:10:25 +0700 Subject: [PATCH 116/446] 1. implemented Label control (again) 2. changed implementation of ControlPresenter to be same as WPF one. Added additional prebuild DataTemplate (for ContentPresenter) 3. added default style for Label control (both Default and Fluent themes) 4. Added sample to TextBox page (not sure needed other sample) 5. Added styles for Label in ControlCatalog/App.xaml --- samples/ControlCatalog/App.xaml | 10 +++ samples/ControlCatalog/Pages/TextBoxPage.xaml | 36 ++++----- src/Avalonia.Controls/Label.cs | 74 +++++++++++++++++++ .../Presenters/ContentPresenter.cs | 25 ++++++- .../Templates/FuncDataTemplate.cs | 28 ++++++- src/Avalonia.Themes.Default/DefaultTheme.xaml | 3 +- src/Avalonia.Themes.Default/Label.xaml | 17 +++++ src/Avalonia.Themes.Fluent/FluentTheme.xaml | 3 +- src/Avalonia.Themes.Fluent/Label.xaml | 17 +++++ 9 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 src/Avalonia.Controls/Label.cs create mode 100644 src/Avalonia.Themes.Default/Label.xaml create mode 100644 src/Avalonia.Themes.Fluent/Label.xaml diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 9bac320c79..6aad44c0d5 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -12,6 +12,16 @@ + + + diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 481a459159..8b07ac3f85 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -2,8 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.TextBoxPage"> - TextBox - A control into which the user can input text + + - + - - resm fonts - - - - - - - - res fonts - - - - - + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls/Label.cs b/src/Avalonia.Controls/Label.cs new file mode 100644 index 0000000000..76708b8f00 --- /dev/null +++ b/src/Avalonia.Controls/Label.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Templates; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace Avalonia.Controls +{ + /// + /// Label control. Focuses on pointer click or access key press (Alt + accessKey) + /// + public class Label : ContentControl + { + /// + /// Defines the Direct property + /// + public static readonly DirectProperty TargetProperty = + AvaloniaProperty.RegisterDirect(nameof(Target), lbl => lbl.Target, (lbl, inp) => lbl.Target = inp); + + /// + /// Label focus target storage field + /// + private IInputElement _target; + + /// + /// Label focus Target + /// + public IInputElement Target + { + get => _target; + set => SetAndRaise(TargetProperty, ref _target, value); + } + + static Label() + { + AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler