From 9c7d91d101aa69c4544d4087f02b73e73b91911e Mon Sep 17 00:00:00 2001 From: Adir Date: Sat, 11 Sep 2021 21:37:55 +0300 Subject: [PATCH 01/15] Added support for Mica backdrop brush in Windows 11 Added support for creating rounded corner backdrop brushes in Windows 10/11 --- src/Windows/Avalonia.Win32/Win32GlManager.cs | 2 +- src/Windows/Avalonia.Win32/Win32Platform.cs | 6 + .../Composition/WinUICompositedWindow.cs | 19 ++- .../Composition/WinUICompositorConnection.cs | 122 ++++++++++----- .../WinUiCompositedWindowSurface.cs | 10 +- src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs | 9 +- src/Windows/Avalonia.Win32/WinRT/winrt.idl | 123 ++++++++++++++- src/Windows/Avalonia.Win32/WindowImpl.cs | 143 ++++++++++-------- 8 files changed, 320 insertions(+), 114 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index e70ea52106..289c100d51 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -27,7 +27,7 @@ namespace Avalonia.Win32 if (egl != null && opts?.UseWindowsUIComposition == true) { - WinUICompositorConnection.TryCreateAndRegister(egl); + WinUICompositorConnection.TryCreateAndRegister(egl, opts.CompositionBackdropCornerRadius); } return egl; diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index c011a458c3..9d56306c59 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -89,6 +89,12 @@ namespace Avalonia /// This is recommended if you need to use AcrylicBlur or acrylic in your applications. /// public bool UseWindowsUIComposition { get; set; } = true; + + /// + /// When enabled, create rounded corner blur brushes + /// If set to zero the brushes will be created using default settings (sharp corners) + /// + public float CompositionBackdropCornerRadius { get; set; } } } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs index 4ae9c08410..1162cf9d70 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs @@ -13,6 +13,8 @@ namespace Avalonia.Win32.WinRT.Composition { private EglContext _syncContext; private readonly object _pumpLock; + private readonly IVisual _micaVisual; + private readonly ICompositionRoundedRectangleGeometry _roundedRectangleGeometry; private readonly IVisual _blurVisual; private ICompositionTarget _compositionTarget; private IVisual _contentVisual; @@ -28,11 +30,14 @@ namespace Avalonia.Win32.WinRT.Composition object pumpLock, ICompositionTarget compositionTarget, ICompositionDrawingSurfaceInterop surfaceInterop, - IVisual contentVisual, IVisual blurVisual) + IVisual contentVisual, IVisual blurVisual, IVisual micaVisual, + ICompositionRoundedRectangleGeometry roundedRectangleGeometry) { _compositor = compositor.CloneReference(); _syncContext = syncContext; _pumpLock = pumpLock; + _micaVisual = micaVisual; + _roundedRectangleGeometry = roundedRectangleGeometry; _blurVisual = blurVisual.CloneReference(); _compositionTarget = compositionTarget.CloneReference(); _contentVisual = contentVisual.CloneReference(); @@ -48,6 +53,7 @@ namespace Avalonia.Win32.WinRT.Composition { _surfaceInterop.Resize(new UnmanagedMethods.POINT { X = size.Width, Y = size.Height }); _contentVisual.SetSize(new Vector2(size.Width, size.Height)); + _roundedRectangleGeometry?.SetSize(new Vector2(size.Width, size.Height)); _size = size; } } @@ -57,7 +63,7 @@ namespace Avalonia.Win32.WinRT.Composition { if (!_syncContext.IsCurrent) throw new InvalidOperationException(); - + var iid = IID_ID3D11Texture2D; void* pTexture; var off = _surfaceInterop.BeginDraw(null, &iid, &pTexture); @@ -72,10 +78,13 @@ namespace Avalonia.Win32.WinRT.Composition _surfaceInterop.EndDraw(); } - public void SetBlur(bool enable) + public void SetBlur(BlurEffect blurEffect) { using (_syncContext.EnsureLocked()) - _blurVisual.SetIsVisible(enable ? 1 : 0); + { + _blurVisual.SetIsVisible(blurEffect == BlurEffect.Acrylic ? 1 : 0); + _micaVisual?.SetIsVisible(blurEffect == BlurEffect.Mica ? 1 : 0); + } } public IDisposable BeginTransaction() @@ -83,7 +92,7 @@ namespace Avalonia.Win32.WinRT.Composition Monitor.Enter(_pumpLock); return Disposable.Create(() => Monitor.Exit(_pumpLock)); } - + public void Dispose() { if (_syncContext == null) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index bc8f5a606c..ebdbae06d7 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Numerics; using System.Runtime.InteropServices; @@ -26,12 +27,15 @@ namespace Avalonia.Win32.WinRT.Composition private EglPlatformOpenGlInterface _gl; private ICompositorDesktopInterop _compositorDesktopInterop; private ICompositionBrush _blurBrush; + private readonly ICompositionBrush _micaBrush; private object _pumpLock = new object(); + private readonly float _backdropCornerRadius; - public WinUICompositorConnection(EglPlatformOpenGlInterface gl, object pumpLock) + public WinUICompositorConnection(EglPlatformOpenGlInterface gl, object pumpLock, float backdropCornerRadius) { _gl = gl; _pumpLock = pumpLock; + _backdropCornerRadius = backdropCornerRadius; _syncContext = _gl.PrimaryEglContext; _angle = (AngleWin32EglDisplay)_gl.Display; _compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor"); @@ -40,15 +44,15 @@ namespace Avalonia.Win32.WinRT.Composition _compositorInterop = _compositor.QueryInterface(); _compositorDesktopInterop = _compositor.QueryInterface(); using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true); - + _device = _compositorInterop.CreateGraphicsDevice(device); - _blurBrush = CreateBlurBrush(); - + _blurBrush = CreateAcrylicBlurBackdropBrush(); + _micaBrush = CreateMicaBackdropBrush(); } public EglPlatformOpenGlInterface Egl => _gl; - static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle) + static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle, float backdropCornerRadius) { var tcs = new TaskCompletionSource(); var pumpLock = new object(); @@ -63,22 +67,19 @@ namespace Avalonia.Win32.WinRT.Composition dwSize = Marshal.SizeOf(), threadType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT }); - connect = new WinUICompositorConnection(angle, pumpLock); + connect = new WinUICompositorConnection(angle, pumpLock, backdropCornerRadius); AvaloniaLocator.CurrentMutable.BindToSelf(connect); AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect); tcs.SetResult(true); - } catch (Exception e) { tcs.SetException(e); return; } + connect.RunLoop(); - }) - { - IsBackground = true - }; + }) { IsBackground = true }; th.SetApartmentState(ApartmentState.STA); th.Start(); return tcs.Task.Result; @@ -93,9 +94,9 @@ namespace Avalonia.Win32.WinRT.Composition { _parent = parent; } + public void Dispose() { - } public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus) @@ -106,6 +107,7 @@ namespace Avalonia.Win32.WinRT.Composition } public MicroComShadow Shadow { get; set; } + public void OnReferencedFromNative() { } @@ -114,12 +116,12 @@ namespace Avalonia.Win32.WinRT.Composition { } } - + private void RunLoop() { { var st = Stopwatch.StartNew(); - using (var act = _compositor5.RequestCommitAsync()) + using (var act = _compositor5.RequestCommitAsync()) act.SetCompleted(new RunLoopHandler(this)); while (true) { @@ -130,7 +132,8 @@ namespace Avalonia.Win32.WinRT.Composition } } - public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle) + public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle, + float backdropCornerRadius) { const int majorRequired = 10; const int buildRequired = 17134; @@ -143,14 +146,12 @@ namespace Avalonia.Win32.WinRT.Composition { try { - TryCreateAndRegisterCore(angle); - return; + TryCreateAndRegisterCore(angle, backdropCornerRadius); } catch (Exception e) { Logger.TryGet(LogEventLevel.Error, "WinUIComposition") ?.Log(null, "Unable to initialize WinUI compositor: {0}", e); - } } @@ -167,17 +168,19 @@ namespace Avalonia.Win32.WinRT.Composition using var sc = _syncContext.EnsureLocked(); using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0); using var target = desktopTarget.QueryInterface(); - - using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized, + + using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), + DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); using var surface = drawingSurface.QueryInterface(); using var surfaceInterop = drawingSurface.QueryInterface(); - + using var surfaceBrush = _compositor.CreateSurfaceBrushWithSurface(surface); using var brush = surfaceBrush.QueryInterface(); using var spriteVisual = _compositor.CreateSpriteVisual(); spriteVisual.SetBrush(brush); + using var visual = spriteVisual.QueryInterface(); using var visual2 = spriteVisual.QueryInterface(); using var container = _compositor.CreateContainerVisual(); @@ -185,47 +188,90 @@ namespace Avalonia.Win32.WinRT.Composition using var containerVisual2 = container.QueryInterface(); containerVisual2.SetRelativeSizeAdjustment(new Vector2(1, 1)); using var containerChildren = container.Children; - + target.SetRoot(containerVisual); - using var blur = CreateBlurVisual(); - + using var blur = CreateBlurVisual(_blurBrush); + IVisual mica = null; + if (_micaBrush != null) + { + mica = CreateBlurVisual(_micaBrush); + containerChildren.InsertAtTop(mica); + } + + var compositionRoundedRectangleGeometry = ClipVisual(blur, mica); + containerChildren.InsertAtTop(blur); containerChildren.InsertAtTop(visual); - - return new WinUICompositedWindow(_syncContext, _compositor, _pumpLock, target, surfaceInterop, visual, blur); + + return new WinUICompositedWindow(_syncContext, _compositor, _pumpLock, target, surfaceInterop, visual, + blur, mica, compositionRoundedRectangleGeometry); } + private ICompositionBrush CreateMicaBackdropBrush() + { + if (Win32Platform.WindowsVersion.Build < 22000) + return null; + + using var compositorWithBlurredWallpaperBackdropBrush = + _compositor.QueryInterface(); + using var blurredWallpaperBackdropBrush = + compositorWithBlurredWallpaperBackdropBrush?.TryCreateBlurredWallpaperBackdropBrush(); + using var micaBackdropBrush = blurredWallpaperBackdropBrush?.QueryInterface(); + return micaBackdropBrush.CloneReference(); + } - private unsafe ICompositionBrush CreateBlurBrush() + private unsafe ICompositionBrush CreateAcrylicBlurBackdropBrush() { - using var backDropParameterFactory = NativeWinRTMethods.CreateActivationFactory( - "Windows.UI.Composition.CompositionEffectSourceParameter"); + using var backDropParameterFactory = + NativeWinRTMethods.CreateActivationFactory( + "Windows.UI.Composition.CompositionEffectSourceParameter"); using var backdropString = new HStringInterop("backdrop"); using var backDropParameter = backDropParameterFactory.Create(backdropString.Handle); using var backDropParameterAsSource = backDropParameter.QueryInterface(); var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource); using var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect); + using var compositionEffectBrush = blurEffectFactory.CreateBrush(); using var backdrop = _compositor2.CreateBackdropBrush(); using var backdropBrush = backdrop.QueryInterface(); - - + var saturateEffect = new SaturationEffect(blurEffect); using var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); using var sat = satEffectFactory.CreateBrush(); - sat.SetSourceParameter(backdropString.Handle, backdropBrush); - return sat.QueryInterface(); + compositionEffectBrush.SetSourceParameter(backdropString.Handle, backdropBrush); + return compositionEffectBrush.QueryInterface(); + } + + private ICompositionRoundedRectangleGeometry ClipVisual(params IVisual[] containerVisuals) + { + if (_backdropCornerRadius == 0) + return null; + using var roundedRectangleGeometry = _compositor5.CreateRoundedRectangleGeometry(); + roundedRectangleGeometry.SetCornerRadius(new Vector2(_backdropCornerRadius, _backdropCornerRadius)); + + using var compositor6 = _compositor.QueryInterface(); + using var compositionGeometry = roundedRectangleGeometry + .QueryInterface(); + + using var geometricClipWithGeometry = + compositor6.CreateGeometricClipWithGeometry(compositionGeometry); + foreach (var visual in containerVisuals) + { + visual?.SetClip(geometricClipWithGeometry.QueryInterface()); + } + + return roundedRectangleGeometry.CloneReference(); } - - private unsafe IVisual CreateBlurVisual() + + private unsafe IVisual CreateBlurVisual(ICompositionBrush compositionBrush) { using var spriteVisual = _compositor.CreateSpriteVisual(); using var visual = spriteVisual.QueryInterface(); using var visual2 = spriteVisual.QueryInterface(); - - - spriteVisual.SetBrush(_blurBrush); + + + spriteVisual.SetBrush(compositionBrush); visual.SetIsVisible(0); visual2.SetRelativeSizeAdjustment(new Vector2(1.0f, 1.0f)); diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs index f59d50860a..4ed882552b 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs @@ -15,7 +15,7 @@ namespace Avalonia.Win32.WinRT.Composition private EglPlatformOpenGlInterface _egl; private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; private IRef _window; - private bool _enableBlur; + private BlurEffect _blurEffect; public WinUiCompositedWindowSurface(WinUICompositorConnection connection, IEglWindowGlPlatformSurfaceInfo info) : base() { @@ -31,7 +31,7 @@ namespace Avalonia.Win32.WinRT.Composition if (_window?.Item == null) { _window = RefCountable.Create(_connection.CreateWindow(_info.Handle)); - _window.Item.SetBlur(_enableBlur); + _window.Item.SetBlur(_blurEffect); } return new CompositionRenderTarget(_egl, _window, _info); @@ -100,10 +100,10 @@ namespace Avalonia.Win32.WinRT.Composition } } - public void SetBlur(bool enable) + public void SetBlur(BlurEffect blurEffect) { - _enableBlur = enable; - _window?.Item?.SetBlur(enable); + _blurEffect = blurEffect; + _window?.Item?.SetBlur(blurEffect); } public void Dispose() diff --git a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs index 81c0e3e185..ea5dcdeeba 100644 --- a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs +++ b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs @@ -1,7 +1,14 @@ namespace Avalonia.Win32.WinRT { + public enum BlurEffect + { + None, + Acrylic, + Mica + } + public interface IBlurHost { - void SetBlur(bool enable); + void SetBlur(BlurEffect enable); } } diff --git a/src/Windows/Avalonia.Win32/WinRT/winrt.idl b/src/Windows/Avalonia.Win32/WinRT/winrt.idl index 929377999c..18a9a26fca 100644 --- a/src/Windows/Avalonia.Win32/WinRT/winrt.idl +++ b/src/Windows/Avalonia.Win32/WinRT/winrt.idl @@ -358,6 +358,12 @@ interface ICompositor2 : IInspectable [overload("CreateStepEasingFunction")] HRESULT CreateStepEasingFunctionWithStepCount([in] INT32 stepCount, [out] [retval] void** result); } +[uuid(0D8FB190-F122-5B8D-9FDD-543B0D8EB7F3)] +interface ICompositorWithBlurredWallpaperBackdropBrush : IInspectable +{ + HRESULT TryCreateBlurredWallpaperBackdropBrush([out] [retval] ICompositionBackdropBrush** result); +} + [uuid(08E05581-1AD1-4F97-9757-402D76E4233B)] interface ISpriteVisual : IInspectable { @@ -520,6 +526,13 @@ enum CompositionCompositeMode MinBlend, } +[contract(Windows.Foundation.UniversalApiContract, 2.0)] +[exclusiveto(Windows.UI.Composition.CompositionClip)] +[uuid(1CCD2A52-CFC7-4ACE-9983-146BB8EB6A3C)] +interface ICompositionClip : IInspectable +{ +} + [uuid(117E202D-A859-4C89-873B-C2AA566788E3)] interface IVisual : IInspectable { @@ -531,8 +544,8 @@ interface IVisual : IInspectable [propput] HRESULT BorderMode([in] CompositionBorderMode value); [propget] HRESULT CenterPoint([out] [retval] Vector3* value); [propput] HRESULT CenterPoint([in] Vector3 value); - [propget] HRESULT Clip([out] [retval]void** value); - [propput] HRESULT Clip([in] void* value); + [propget] HRESULT Clip([out] [retval] ICompositionClip** value); + [propput] HRESULT Clip([in] ICompositionClip* value); [propget] HRESULT CompositeMode([out] [retval] CompositionCompositeMode* value); [propput] HRESULT CompositeMode([in] CompositionCompositeMode value); [propget] HRESULT IsVisible([out] [retval] boolean* value); @@ -692,6 +705,99 @@ interface ICompositionScopedBatch : IInspectable [eventremove] HRESULT RemoveCompleted([in] int token); } +[contract(Windows.Foundation.UniversalApiContract, 6.0)] +[exclusiveto(Windows.UI.Composition.CompositionRoundedRectangleGeometry)] +[uuid(8770C822-1D50-4B8B-B013-7C9A0E46935F)] +interface ICompositionRoundedRectangleGeometry : IInspectable +{ + [propget] HRESULT CornerRadius([out] [retval] Vector2* value); + [propput] HRESULT CornerRadius([in] Vector2 value); + [propget] HRESULT Offset([out] [retval] Vector2* value); + [propput] HRESULT Offset([in] Vector2 value); + [propget] HRESULT Size([out] [retval] Vector2* value); + [propput] HRESULT Size([in] Vector2 value); +} + +[contract(Windows.Foundation.UniversalApiContract, 6.0)] +[exclusiveto(Windows.UI.Composition.CompositionGeometry)] +[uuid(E985217C-6A17-4207-ABD8-5FD3DD612A9D)] +interface ICompositionGeometry : IInspectable +{ + [propget] HRESULT TrimEnd([out] [retval] FLOAT* value); + [propput] HRESULT TrimEnd([in] FLOAT value); + [propget] HRESULT TrimOffset([out] [retval] FLOAT* value); + [propput] HRESULT TrimOffset([in] FLOAT value); + [propget] HRESULT TrimStart([out] [retval] FLOAT* value); + [propput] HRESULT TrimStart([in] FLOAT value); +} + +[uuid(401B61BB-0007-4363-B1F3-6BCC003FB83E)] +interface ICompositionSpriteShape : IInspectable +{ + [propget] HRESULT GetFillBrush([out] [retval] ICompositionBrush** value); + [propput] HRESULT SetFillBrush([in] ICompositionBrush* value); + [propget] HRESULT Geometry([out] [retval] ICompositionGeometry** value); + [propput] HRESULT Geometry([in] ICompositionGeometry* value); + [propget] HRESULT IsStrokeNonScaling([out] [retval] boolean* value); + [propput] HRESULT IsStrokeNonScaling([in] boolean value); + [propget] HRESULT StrokeBrush([out] [retval] ICompositionBrush** value); + [propput] HRESULT StrokeBrush([in] ICompositionBrush* value); + [propget] HRESULT StrokeDashArray(); + [propget] HRESULT StrokeDashCap(); + [propput] HRESULT StrokeDashCap(); + [propget] HRESULT StrokeDashOffset(); + [propput] HRESULT StrokeDashOffset(); + [propget] HRESULT StrokeEndCap(); + [propput] HRESULT StrokeEndCap(); + [propget] HRESULT StrokeLineJoin(); + [propput] HRESULT StrokeLineJoin(); + [propget] HRESULT StrokeMiterLimit(); + [propput] HRESULT StrokeMiterLimit(); + [propget] HRESULT StrokeStartCap(); + [propput] HRESULT StrokeStartCap(); + [propget] HRESULT StrokeThickness(); + [propput] HRESULT StrokeThickness(); +} + +[contract(Windows.Foundation.UniversalApiContract, 6.0)] +[exclusiveto(Windows.UI.Composition.CompositionShape)] +[uuid(B47CE2F7-9A88-42C4-9E87-2E500CA8688C)] +interface ICompositionShape : IInspectable +{ + [propget] HRESULT CenterPoint([out] [retval] Vector2* value); + [propput] HRESULT CenterPoint([in] Vector2 value); +} + +[uuid(42d4219a-be1b-5091-8f1e-90270840fc2d)] +interface IVectorOfCompositionShape : IInspectable +{ + HRESULT GetAt(); + [propget] HRESULT GetSize(); + HRESULT GetView(); + HRESULT IndexOf(); + HRESULT SetAt(); + HRESULT InsertAt(); + HRESULT RemoveAt(); + HRESULT Append([in] ICompositionShape* value); + HRESULT RemoveAtEnd(); + HRESULT Clear(); +} + +[contract(Windows.Foundation.UniversalApiContract, 7.0)] +[exclusiveto(Windows.UI.Composition.CompositionGeometricClip)] +[uuid(C840B581-81C9-4444-A2C1-CCAECE3A50E5)] +interface ICompositionGeometricClip : IInspectable +{ + [propget] HRESULT Geometry([out] [retval] ICompositionGeometry** value); + [propput] HRESULT Geometry([in] ICompositionGeometry* value); +} + +[uuid(F2BD13C3-BA7E-4B0F-9126-FFB7536B8176)] +interface IShapeVisual : IInspectable +{ + [propget] HRESULT Shapes([out] [retval] IUnknown** value); +} + [uuid(48EA31AD-7FCD-4076-A79C-90CC4B852C9B)] interface ICompositor5 : IInspectable { @@ -709,10 +815,19 @@ interface ICompositor5 : IInspectable [overload("CreatePathGeometry")] HRESULT CreatePathGeometryWithPath([in] void* path, [out] [retval] void** result); HRESULT CreatePathKeyFrameAnimation([out] [retval] void** result); HRESULT CreateRectangleGeometry([out] [retval] void** result); - HRESULT CreateRoundedRectangleGeometry([out] [retval] void** result); - HRESULT CreateShapeVisual([out] [retval] void** result); + HRESULT CreateRoundedRectangleGeometry([out] [retval] ICompositionRoundedRectangleGeometry** result); + HRESULT CreateShapeVisual([out] [retval] IShapeVisual** result); [overload("CreateSpriteShape")] HRESULT CreateSpriteShape([out] [retval] void** result); [overload("CreateSpriteShape")] HRESULT CreateSpriteShapeWithGeometry([in] void* geometry, [out] [retval] void** result); HRESULT CreateViewBox([out] [retval] void** result); HRESULT RequestCommitAsync([out] [retval] IAsyncAction** operation); } + +[contract(Windows.Foundation.UniversalApiContract, 7.0)] +[exclusiveto(Windows.UI.Composition.Compositor)] +[uuid(7A38B2BD-CEC8-4EEB-830F-D8D07AEDEBC3)] +interface ICompositor6 : IInspectable +{ + [overload("CreateGeometricClip")] HRESULT CreateGeometricClip([out] [retval] ICompositionGeometricClip** result); + [overload("CreateGeometricClip")] HRESULT CreateGeometricClipWithGeometry([in] ICompositionGeometry* geometry, [out] [retval] ICompositionGeometricClip** result); +} diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 8fc25f8cfa..0a0f600520 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -82,7 +82,7 @@ namespace Avalonia.Win32 private Size _minSize; private Size _maxSize; private POINT _maxTrackSize; - private WindowImpl _parent; + private WindowImpl _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; private bool _isCloseRequested; private bool _shown; @@ -105,9 +105,7 @@ namespace Avalonia.Win32 _windowProperties = new WindowProperties { - ShowInTaskbar = false, - IsResizable = true, - Decorations = SystemDecorations.Full + ShowInTaskbar = false, IsResizable = true, Decorations = SystemDecorations.Full }; _rendererLock = new ManagedDeferredRendererLock(); @@ -116,13 +114,13 @@ namespace Avalonia.Win32 var compositionConnector = AvaloniaLocator.Current.GetService(); _isUsingComposition = compositionConnector is { } && - glPlatform is EglPlatformOpenGlInterface egl && - egl.Display is AngleWin32EglDisplay angleDisplay && - angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; + glPlatform is EglPlatformOpenGlInterface egl && + egl.Display is AngleWin32EglDisplay angleDisplay && + angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; CreateWindow(); _framebuffer = new FramebufferManager(_hwnd); - + if (glPlatform != null) { if (_isUsingComposition) @@ -168,7 +166,7 @@ namespace Avalonia.Win32 public Action PositionChanged { get; set; } public Action WindowStateChanged { get; set; } - + public Action LostFocus { get; set; } public Action TransparencyLevelChanged { get; set; } @@ -224,7 +222,8 @@ namespace Avalonia.Win32 return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling; } - DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT))); + DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, + Marshal.SizeOf(typeof(RECT))); return new Size(rect.Width, rect.Height) / RenderScaling; } } @@ -233,7 +232,8 @@ namespace Avalonia.Win32 public IPlatformHandle Handle { get; private set; } - public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling); + public virtual Size MaxAutoSizeHint => + new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling); public IMouseDevice MouseDevice => _mouseDevice; @@ -241,7 +241,7 @@ namespace Avalonia.Win32 { get { - if(_isFullScreenActive) + if (_isFullScreenActive) { return WindowState.FullScreen; } @@ -264,7 +264,7 @@ namespace Avalonia.Win32 ShowWindow(value, true); } - _showWindowState = value; + _showWindowState = value; } } @@ -272,7 +272,7 @@ namespace Avalonia.Win32 protected IntPtr Hwnd => _hwnd; - public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel) + public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { TransparencyLevel = EnableBlur(transparencyLevel); } @@ -312,12 +312,12 @@ namespace Avalonia.Win32 } var blurInfo = new DWM_BLURBEHIND(false); - + if (transparencyLevel == WindowTransparencyLevel.Blur) { blurInfo = new DWM_BLURBEHIND(true); } - + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); if (transparencyLevel == WindowTransparencyLevel.Transparent) @@ -373,13 +373,20 @@ namespace Avalonia.Win32 { if (_isUsingComposition) { - _blurHost?.SetBlur(transparencyLevel >= WindowTransparencyLevel.Blur); + _blurHost?.SetBlur(transparencyLevel switch + { + WindowTransparencyLevel.Mica => BlurEffect.Mica, + WindowTransparencyLevel.AcrylicBlur => BlurEffect.Acrylic, + WindowTransparencyLevel.Blur => BlurEffect.Acrylic, + _ => BlurEffect.None + }); return transparencyLevel; } else { - bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628; + bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || + Win32Platform.WindowsVersion.Build >= 19628; var accent = new AccentPolicy(); var accentStructSize = Marshal.SizeOf(accent); @@ -530,7 +537,7 @@ namespace Avalonia.Win32 { BeforeCloseCleanup(true); } - + DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } @@ -596,7 +603,7 @@ namespace Avalonia.Win32 public void SetParent(IWindowImpl parent) { _parent = (WindowImpl)parent; - + var parentHwnd = _parent?._hwnd ?? IntPtr.Zero; if (parentHwnd == IntPtr.Zero && !_windowProperties.ShowInTaskbar) @@ -707,7 +714,7 @@ namespace Avalonia.Win32 _isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0, atom, null, - (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN, + (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int)WindowStyles.WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, @@ -763,7 +770,7 @@ namespace Avalonia.Win32 } if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8) - { + { var monitor = MonitorFromWindow( _hwnd, MONITOR.MONITOR_DEFAULTTONEAREST); @@ -809,7 +816,9 @@ namespace Avalonia.Win32 // Set new window style and size. SetStyle(current & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false); - SetExtendedStyle(currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); + SetExtendedStyle( + currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | + WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); // On expand, if we're given a window_rect, grow to it, otherwise do // not resize. @@ -819,8 +828,9 @@ namespace Avalonia.Win32 var window_rect = monitor_info.rcMonitor.ToPixelRect(); SetWindowPos(_hwnd, IntPtr.Zero, window_rect.X, window_rect.Y, - window_rect.Width, window_rect.Height, - SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); + window_rect.Width, window_rect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | + SetWindowPosFlags.SWP_FRAMECHANGED); _isFullScreenActive = true; } @@ -839,21 +849,22 @@ namespace Avalonia.Win32 var new_rect = _savedWindowInfo.WindowRect.ToPixelRect(); SetWindowPos(_hwnd, IntPtr.Zero, new_rect.X, new_rect.Y, new_rect.Width, - new_rect.Height, - SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); + new_rect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | + SetWindowPosFlags.SWP_FRAMECHANGED); UpdateWindowProperties(_windowProperties, true); } TaskBarList.MarkFullscreen(_hwnd, fullscreen); - + ExtendClientArea(); } private MARGINS UpdateExtendMargins() { RECT borderThickness = new RECT(); - RECT borderCaptionThickness = new RECT(); + RECT borderCaptionThickness = new RECT(); AdjustWindowRectEx(ref borderCaptionThickness, (uint)(GetStyle()), false, 0); AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle() & ~WindowStyles.WS_CAPTION), false, 0); @@ -862,7 +873,8 @@ namespace Avalonia.Win32 borderCaptionThickness.left *= -1; borderCaptionThickness.top *= -1; - bool wantsTitleBar = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) || _extendTitleBarHint == -1; + bool wantsTitleBar = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) || + _extendTitleBarHint == -1; if (!wantsTitleBar) { @@ -876,15 +888,22 @@ namespace Avalonia.Win32 if (_extendTitleBarHint != -1) { - borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling); + borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling); } - margins.cyTopHeight = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1; + margins.cyTopHeight = + _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && + !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? + borderCaptionThickness.top : + 1; if (WindowState == WindowState.Maximized) { - _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, 0, 0); - _offScreenMargin = new Thickness(borderThickness.left / RenderScaling, borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, borderThickness.bottom / RenderScaling); + _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, + 0, 0); + _offScreenMargin = new Thickness(borderThickness.left / RenderScaling, + borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, + borderThickness.bottom / RenderScaling); } else { @@ -901,12 +920,13 @@ namespace Avalonia.Win32 { return; } - + if (DwmIsCompositionEnabled(out bool compositionEnabled) < 0 || !compositionEnabled) { _isClientAreaExtended = false; return; } + GetClientRect(_hwnd, out var rcClient); GetWindowRect(_hwnd, out var rcWindow); @@ -929,12 +949,14 @@ namespace Avalonia.Win32 _offScreenMargin = new Thickness(); _extendedMargins = new Thickness(); - - Resize(new Size(rcWindow.Width/ RenderScaling, rcWindow.Height / RenderScaling), PlatformResizeReason.Layout); + + Resize(new Size(rcWindow.Width / RenderScaling, rcWindow.Height / RenderScaling), + PlatformResizeReason.Layout); } - if(!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && - !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome))) + if (!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && + !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints + .PreferSystemChrome))) { EnableCloseButton(_hwnd); } @@ -949,12 +971,12 @@ namespace Avalonia.Win32 private void ShowWindow(WindowState state, bool activate) { _shown = true; - + if (_isClientAreaExtended) { ExtendClientArea(); } - + ShowWindowCommand? command; var newWindowProperties = _windowProperties; @@ -972,7 +994,7 @@ namespace Avalonia.Win32 case WindowState.Normal: newWindowProperties.IsFullScreen = false; - command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : + command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : activate ? ShowWindowCommand.Normal : ShowWindowCommand.ShowNoActivate; break; @@ -1002,7 +1024,7 @@ namespace Avalonia.Win32 SetFocus(_hwnd); } } - + private void BeforeCloseCleanup(bool isDisposing) { // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 @@ -1020,7 +1042,7 @@ namespace Avalonia.Win32 // Our window closed callback will set enabled state to a correct value after child window gets destroyed. _parent.SetEnabled(true); } - + // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. if (wasActive) { @@ -1047,7 +1069,7 @@ namespace Avalonia.Win32 SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW); } } - } + } private WindowStyles GetWindowStateStyles() { @@ -1200,10 +1222,7 @@ namespace Avalonia.Win32 var margins = new MARGINS { - cyBottomHeight = margin, - cxRightWidth = margin, - cxLeftWidth = margin, - cyTopHeight = margin + cyBottomHeight = margin, cxRightWidth = margin, cxLeftWidth = margin, cyTopHeight = margin }; DwmExtendFrameIntoClientArea(_hwnd, ref margins); @@ -1224,7 +1243,7 @@ namespace Avalonia.Win32 SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); } - } + } } private const int MF_BYCOMMAND = 0x0; @@ -1238,12 +1257,13 @@ namespace Avalonia.Win32 void DisableCloseButton(IntPtr hwnd) { EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, - MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } + void EnableCloseButton(IntPtr hwnd) { EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, - MF_BYCOMMAND | MF_ENABLED); + MF_BYCOMMAND | MF_ENABLED); } #if USE_MANAGED_DRAG @@ -1274,9 +1294,9 @@ namespace Avalonia.Win32 public void SetExtendClientAreaToDecorationsHint(bool hint) { _isClientAreaExtended = hint; - - ExtendClientArea(); - } + + ExtendClientArea(); + } public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) { @@ -1284,7 +1304,7 @@ namespace Avalonia.Win32 ExtendClientArea(); } - + /// public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) { @@ -1298,9 +1318,11 @@ namespace Avalonia.Win32 /// public Action ExtendClientAreaToDecorationsChanged { get; set; } - + /// - public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome); + public bool NeedsManagedDecorations => _isClientAreaExtended && + _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints + .PreferSystemChrome); /// public Thickness ExtendedMargins => _extendedMargins; @@ -1309,7 +1331,8 @@ namespace Avalonia.Win32 public Thickness OffScreenMargin => _offScreenMargin; /// - public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0); + public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = + new AcrylicPlatformCompensationLevels(1, 0.8, 0); private ResizeReasonScope SetResizeReason(PlatformResizeReason reason) { @@ -1337,7 +1360,7 @@ namespace Avalonia.Win32 { private readonly WindowImpl _owner; private readonly PlatformResizeReason _restore; - + public ResizeReasonScope(WindowImpl owner, PlatformResizeReason restore) { _owner = owner; From e61147edc181a9fb1ff1bed5a5b4927ab376cdc4 Mon Sep 17 00:00:00 2001 From: Adir Date: Sat, 11 Sep 2021 21:53:12 +0300 Subject: [PATCH 02/15] Added Mica enum value to WindowTransparencyLevel.cs Added Mica as selected option --- samples/ControlCatalog/MainView.xaml | 1 + samples/ControlCatalog/MainWindow.xaml | 1 + samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml | 1 + src/Avalonia.Controls/WindowTransparencyLevel.cs | 7 ++++++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 6537c470d5..f61b59e6cd 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -98,6 +98,7 @@ Transparent Blur AcrylicBlur + Mica diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index a107ee2163..ee42e7a54b 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -12,6 +12,7 @@ ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}" TransparencyLevelHint="{Binding TransparencyLevel}" x:Name="MainWindow" + Background="Transparent" x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"> diff --git a/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml b/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml index b90f43c3b6..caab42e98c 100644 --- a/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml +++ b/samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml @@ -14,6 +14,7 @@ Transparent Blur AcrylicBlur + Mica diff --git a/src/Avalonia.Controls/WindowTransparencyLevel.cs b/src/Avalonia.Controls/WindowTransparencyLevel.cs index ce7c03efbb..f416b5de91 100644 --- a/src/Avalonia.Controls/WindowTransparencyLevel.cs +++ b/src/Avalonia.Controls/WindowTransparencyLevel.cs @@ -20,6 +20,11 @@ /// /// The window background is a blur-behind with a high blur radius. This level may fallback to Blur. /// - AcrylicBlur + AcrylicBlur, + + /// + /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 + /// + Mica } } From c03ff60b2d7056685953a32efa615f805ac83ce1 Mon Sep 17 00:00:00 2001 From: Adir Date: Sat, 11 Sep 2021 22:07:14 +0300 Subject: [PATCH 03/15] Reverted code formatting --- .../Composition/WinUICompositedWindow.cs | 4 +- .../Composition/WinUICompositorConnection.cs | 47 +++--- src/Windows/Avalonia.Win32/WindowImpl.cs | 135 ++++++++---------- 3 files changed, 85 insertions(+), 101 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs index 1162cf9d70..a09918a3a6 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs @@ -63,7 +63,7 @@ namespace Avalonia.Win32.WinRT.Composition { if (!_syncContext.IsCurrent) throw new InvalidOperationException(); - + var iid = IID_ID3D11Texture2D; void* pTexture; var off = _surfaceInterop.BeginDraw(null, &iid, &pTexture); @@ -92,7 +92,7 @@ namespace Avalonia.Win32.WinRT.Composition Monitor.Enter(_pumpLock); return Disposable.Create(() => Monitor.Exit(_pumpLock)); } - + public void Dispose() { if (_syncContext == null) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index ebdbae06d7..f5706b6fb5 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -44,7 +44,7 @@ namespace Avalonia.Win32.WinRT.Composition _compositorInterop = _compositor.QueryInterface(); _compositorDesktopInterop = _compositor.QueryInterface(); using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true); - + _device = _compositorInterop.CreateGraphicsDevice(device); _blurBrush = CreateAcrylicBlurBackdropBrush(); _micaBrush = CreateMicaBackdropBrush(); @@ -71,15 +71,18 @@ namespace Avalonia.Win32.WinRT.Composition AvaloniaLocator.CurrentMutable.BindToSelf(connect); AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect); tcs.SetResult(true); + } catch (Exception e) { tcs.SetException(e); return; } - connect.RunLoop(); - }) { IsBackground = true }; + }) + { + IsBackground = true + }; th.SetApartmentState(ApartmentState.STA); th.Start(); return tcs.Task.Result; @@ -94,9 +97,9 @@ namespace Avalonia.Win32.WinRT.Composition { _parent = parent; } - public void Dispose() { + } public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus) @@ -107,7 +110,6 @@ namespace Avalonia.Win32.WinRT.Composition } public MicroComShadow Shadow { get; set; } - public void OnReferencedFromNative() { } @@ -116,12 +118,12 @@ namespace Avalonia.Win32.WinRT.Composition { } } - + private void RunLoop() { { var st = Stopwatch.StartNew(); - using (var act = _compositor5.RequestCommitAsync()) + using (var act = _compositor5.RequestCommitAsync()) act.SetCompleted(new RunLoopHandler(this)); while (true) { @@ -152,6 +154,7 @@ namespace Avalonia.Win32.WinRT.Composition { Logger.TryGet(LogEventLevel.Error, "WinUIComposition") ?.Log(null, "Unable to initialize WinUI compositor: {0}", e); + } } @@ -168,19 +171,17 @@ namespace Avalonia.Win32.WinRT.Composition using var sc = _syncContext.EnsureLocked(); using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0); using var target = desktopTarget.QueryInterface(); - - using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), - DirectXPixelFormat.B8G8R8A8UIntNormalized, + + using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); using var surface = drawingSurface.QueryInterface(); using var surfaceInterop = drawingSurface.QueryInterface(); - + using var surfaceBrush = _compositor.CreateSurfaceBrushWithSurface(surface); using var brush = surfaceBrush.QueryInterface(); using var spriteVisual = _compositor.CreateSpriteVisual(); spriteVisual.SetBrush(brush); - using var visual = spriteVisual.QueryInterface(); using var visual2 = spriteVisual.QueryInterface(); using var container = _compositor.CreateContainerVisual(); @@ -188,7 +189,7 @@ namespace Avalonia.Win32.WinRT.Composition using var containerVisual2 = container.QueryInterface(); containerVisual2.SetRelativeSizeAdjustment(new Vector2(1, 1)); using var containerChildren = container.Children; - + target.SetRoot(containerVisual); using var blur = CreateBlurVisual(_blurBrush); @@ -200,10 +201,10 @@ namespace Avalonia.Win32.WinRT.Composition } var compositionRoundedRectangleGeometry = ClipVisual(blur, mica); - + containerChildren.InsertAtTop(blur); containerChildren.InsertAtTop(visual); - + return new WinUICompositedWindow(_syncContext, _compositor, _pumpLock, target, surfaceInterop, visual, blur, mica, compositionRoundedRectangleGeometry); } @@ -223,9 +224,8 @@ namespace Avalonia.Win32.WinRT.Composition private unsafe ICompositionBrush CreateAcrylicBlurBackdropBrush() { - using var backDropParameterFactory = - NativeWinRTMethods.CreateActivationFactory( - "Windows.UI.Composition.CompositionEffectSourceParameter"); + using var backDropParameterFactory = NativeWinRTMethods.CreateActivationFactory( + "Windows.UI.Composition.CompositionEffectSourceParameter"); using var backdropString = new HStringInterop("backdrop"); using var backDropParameter = backDropParameterFactory.Create(backdropString.Handle); @@ -235,7 +235,8 @@ namespace Avalonia.Win32.WinRT.Composition using var compositionEffectBrush = blurEffectFactory.CreateBrush(); using var backdrop = _compositor2.CreateBackdropBrush(); using var backdropBrush = backdrop.QueryInterface(); - + + var saturateEffect = new SaturationEffect(blurEffect); using var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); using var sat = satEffectFactory.CreateBrush(); @@ -259,8 +260,8 @@ namespace Avalonia.Win32.WinRT.Composition foreach (var visual in containerVisuals) { visual?.SetClip(geometricClipWithGeometry.QueryInterface()); - } - + } + return roundedRectangleGeometry.CloneReference(); } @@ -269,8 +270,8 @@ namespace Avalonia.Win32.WinRT.Composition using var spriteVisual = _compositor.CreateSpriteVisual(); using var visual = spriteVisual.QueryInterface(); using var visual2 = spriteVisual.QueryInterface(); - - + + spriteVisual.SetBrush(compositionBrush); visual.SetIsVisible(0); visual2.SetRelativeSizeAdjustment(new Vector2(1.0f, 1.0f)); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 0a0f600520..651d3adb11 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -82,7 +82,7 @@ namespace Avalonia.Win32 private Size _minSize; private Size _maxSize; private POINT _maxTrackSize; - private WindowImpl _parent; + private WindowImpl _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; private bool _isCloseRequested; private bool _shown; @@ -105,7 +105,9 @@ namespace Avalonia.Win32 _windowProperties = new WindowProperties { - ShowInTaskbar = false, IsResizable = true, Decorations = SystemDecorations.Full + ShowInTaskbar = false, + IsResizable = true, + Decorations = SystemDecorations.Full }; _rendererLock = new ManagedDeferredRendererLock(); @@ -114,13 +116,13 @@ namespace Avalonia.Win32 var compositionConnector = AvaloniaLocator.Current.GetService(); _isUsingComposition = compositionConnector is { } && - glPlatform is EglPlatformOpenGlInterface egl && - egl.Display is AngleWin32EglDisplay angleDisplay && - angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; + glPlatform is EglPlatformOpenGlInterface egl && + egl.Display is AngleWin32EglDisplay angleDisplay && + angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; CreateWindow(); _framebuffer = new FramebufferManager(_hwnd); - + if (glPlatform != null) { if (_isUsingComposition) @@ -166,7 +168,7 @@ namespace Avalonia.Win32 public Action PositionChanged { get; set; } public Action WindowStateChanged { get; set; } - + public Action LostFocus { get; set; } public Action TransparencyLevelChanged { get; set; } @@ -222,8 +224,7 @@ namespace Avalonia.Win32 return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling; } - DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, - Marshal.SizeOf(typeof(RECT))); + DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT))); return new Size(rect.Width, rect.Height) / RenderScaling; } } @@ -232,8 +233,7 @@ namespace Avalonia.Win32 public IPlatformHandle Handle { get; private set; } - public virtual Size MaxAutoSizeHint => - new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling); + public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / RenderScaling, _maxTrackSize.Y / RenderScaling); public IMouseDevice MouseDevice => _mouseDevice; @@ -241,7 +241,7 @@ namespace Avalonia.Win32 { get { - if (_isFullScreenActive) + if(_isFullScreenActive) { return WindowState.FullScreen; } @@ -264,7 +264,7 @@ namespace Avalonia.Win32 ShowWindow(value, true); } - _showWindowState = value; + _showWindowState = value; } } @@ -272,7 +272,7 @@ namespace Avalonia.Win32 protected IntPtr Hwnd => _hwnd; - public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) + public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel) { TransparencyLevel = EnableBlur(transparencyLevel); } @@ -312,12 +312,12 @@ namespace Avalonia.Win32 } var blurInfo = new DWM_BLURBEHIND(false); - + if (transparencyLevel == WindowTransparencyLevel.Blur) { blurInfo = new DWM_BLURBEHIND(true); } - + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); if (transparencyLevel == WindowTransparencyLevel.Transparent) @@ -385,8 +385,7 @@ namespace Avalonia.Win32 } else { - bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || - Win32Platform.WindowsVersion.Build >= 19628; + bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628; var accent = new AccentPolicy(); var accentStructSize = Marshal.SizeOf(accent); @@ -537,7 +536,7 @@ namespace Avalonia.Win32 { BeforeCloseCleanup(true); } - + DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } @@ -603,7 +602,7 @@ namespace Avalonia.Win32 public void SetParent(IWindowImpl parent) { _parent = (WindowImpl)parent; - + var parentHwnd = _parent?._hwnd ?? IntPtr.Zero; if (parentHwnd == IntPtr.Zero && !_windowProperties.ShowInTaskbar) @@ -714,7 +713,7 @@ namespace Avalonia.Win32 _isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0, atom, null, - (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int)WindowStyles.WS_CLIPCHILDREN, + (int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, @@ -770,7 +769,7 @@ namespace Avalonia.Win32 } if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8) - { + { var monitor = MonitorFromWindow( _hwnd, MONITOR.MONITOR_DEFAULTTONEAREST); @@ -816,9 +815,7 @@ namespace Avalonia.Win32 // Set new window style and size. SetStyle(current & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false); - SetExtendedStyle( - currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | - WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); + SetExtendedStyle(currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); // On expand, if we're given a window_rect, grow to it, otherwise do // not resize. @@ -828,9 +825,8 @@ namespace Avalonia.Win32 var window_rect = monitor_info.rcMonitor.ToPixelRect(); SetWindowPos(_hwnd, IntPtr.Zero, window_rect.X, window_rect.Y, - window_rect.Width, window_rect.Height, - SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | - SetWindowPosFlags.SWP_FRAMECHANGED); + window_rect.Width, window_rect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); _isFullScreenActive = true; } @@ -849,22 +845,21 @@ namespace Avalonia.Win32 var new_rect = _savedWindowInfo.WindowRect.ToPixelRect(); SetWindowPos(_hwnd, IntPtr.Zero, new_rect.X, new_rect.Y, new_rect.Width, - new_rect.Height, - SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | - SetWindowPosFlags.SWP_FRAMECHANGED); + new_rect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); UpdateWindowProperties(_windowProperties, true); } TaskBarList.MarkFullscreen(_hwnd, fullscreen); - + ExtendClientArea(); } private MARGINS UpdateExtendMargins() { RECT borderThickness = new RECT(); - RECT borderCaptionThickness = new RECT(); + RECT borderCaptionThickness = new RECT(); AdjustWindowRectEx(ref borderCaptionThickness, (uint)(GetStyle()), false, 0); AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle() & ~WindowStyles.WS_CAPTION), false, 0); @@ -873,8 +868,7 @@ namespace Avalonia.Win32 borderCaptionThickness.left *= -1; borderCaptionThickness.top *= -1; - bool wantsTitleBar = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) || - _extendTitleBarHint == -1; + bool wantsTitleBar = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) || _extendTitleBarHint == -1; if (!wantsTitleBar) { @@ -888,22 +882,15 @@ namespace Avalonia.Win32 if (_extendTitleBarHint != -1) { - borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling); + borderCaptionThickness.top = (int)(_extendTitleBarHint * RenderScaling); } - margins.cyTopHeight = - _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && - !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? - borderCaptionThickness.top : - 1; + margins.cyTopHeight = _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome) ? borderCaptionThickness.top : 1; if (WindowState == WindowState.Maximized) { - _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, - 0, 0); - _offScreenMargin = new Thickness(borderThickness.left / RenderScaling, - borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, - borderThickness.bottom / RenderScaling); + _extendedMargins = new Thickness(0, (borderCaptionThickness.top - borderThickness.top) / RenderScaling, 0, 0); + _offScreenMargin = new Thickness(borderThickness.left / RenderScaling, borderThickness.top / RenderScaling, borderThickness.right / RenderScaling, borderThickness.bottom / RenderScaling); } else { @@ -920,13 +907,12 @@ namespace Avalonia.Win32 { return; } - + if (DwmIsCompositionEnabled(out bool compositionEnabled) < 0 || !compositionEnabled) { _isClientAreaExtended = false; return; } - GetClientRect(_hwnd, out var rcClient); GetWindowRect(_hwnd, out var rcWindow); @@ -949,14 +935,12 @@ namespace Avalonia.Win32 _offScreenMargin = new Thickness(); _extendedMargins = new Thickness(); - - Resize(new Size(rcWindow.Width / RenderScaling, rcWindow.Height / RenderScaling), - PlatformResizeReason.Layout); + + Resize(new Size(rcWindow.Width/ RenderScaling, rcWindow.Height / RenderScaling), PlatformResizeReason.Layout); } - if (!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && - !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints - .PreferSystemChrome))) + if(!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) && + !_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome))) { EnableCloseButton(_hwnd); } @@ -971,12 +955,12 @@ namespace Avalonia.Win32 private void ShowWindow(WindowState state, bool activate) { _shown = true; - + if (_isClientAreaExtended) { ExtendClientArea(); } - + ShowWindowCommand? command; var newWindowProperties = _windowProperties; @@ -994,7 +978,7 @@ namespace Avalonia.Win32 case WindowState.Normal: newWindowProperties.IsFullScreen = false; - command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : + command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : activate ? ShowWindowCommand.Normal : ShowWindowCommand.ShowNoActivate; break; @@ -1024,7 +1008,7 @@ namespace Avalonia.Win32 SetFocus(_hwnd); } } - + private void BeforeCloseCleanup(bool isDisposing) { // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 @@ -1042,7 +1026,7 @@ namespace Avalonia.Win32 // Our window closed callback will set enabled state to a correct value after child window gets destroyed. _parent.SetEnabled(true); } - + // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. if (wasActive) { @@ -1069,7 +1053,7 @@ namespace Avalonia.Win32 SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW); } } - } + } private WindowStyles GetWindowStateStyles() { @@ -1222,7 +1206,10 @@ namespace Avalonia.Win32 var margins = new MARGINS { - cyBottomHeight = margin, cxRightWidth = margin, cxLeftWidth = margin, cyTopHeight = margin + cyBottomHeight = margin, + cxRightWidth = margin, + cxLeftWidth = margin, + cyTopHeight = margin }; DwmExtendFrameIntoClientArea(_hwnd, ref margins); @@ -1243,7 +1230,7 @@ namespace Avalonia.Win32 SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); } - } + } } private const int MF_BYCOMMAND = 0x0; @@ -1257,13 +1244,12 @@ namespace Avalonia.Win32 void DisableCloseButton(IntPtr hwnd) { EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, - MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); } - void EnableCloseButton(IntPtr hwnd) { EnableMenuItem(GetSystemMenu(hwnd, false), SC_CLOSE, - MF_BYCOMMAND | MF_ENABLED); + MF_BYCOMMAND | MF_ENABLED); } #if USE_MANAGED_DRAG @@ -1294,9 +1280,9 @@ namespace Avalonia.Win32 public void SetExtendClientAreaToDecorationsHint(bool hint) { _isClientAreaExtended = hint; - - ExtendClientArea(); - } + + ExtendClientArea(); + } public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) { @@ -1304,7 +1290,7 @@ namespace Avalonia.Win32 ExtendClientArea(); } - + /// public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) { @@ -1318,11 +1304,9 @@ namespace Avalonia.Win32 /// public Action ExtendClientAreaToDecorationsChanged { get; set; } - + /// - public bool NeedsManagedDecorations => _isClientAreaExtended && - _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints - .PreferSystemChrome); + public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome); /// public Thickness ExtendedMargins => _extendedMargins; @@ -1331,8 +1315,7 @@ namespace Avalonia.Win32 public Thickness OffScreenMargin => _offScreenMargin; /// - public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = - new AcrylicPlatformCompensationLevels(1, 0.8, 0); + public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0); private ResizeReasonScope SetResizeReason(PlatformResizeReason reason) { @@ -1360,7 +1343,7 @@ namespace Avalonia.Win32 { private readonly WindowImpl _owner; private readonly PlatformResizeReason _restore; - + public ResizeReasonScope(WindowImpl owner, PlatformResizeReason restore) { _owner = owner; From fdb453cfbfa4656307cac3343e521304628437bc Mon Sep 17 00:00:00 2001 From: Adir Date: Sat, 11 Sep 2021 22:09:17 +0300 Subject: [PATCH 04/15] Added missing return on WinUICompositorConnection.cs --- .../WinRT/Composition/WinUICompositorConnection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index f5706b6fb5..21de6f169b 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -149,6 +149,7 @@ namespace Avalonia.Win32.WinRT.Composition try { TryCreateAndRegisterCore(angle, backdropCornerRadius); + return; } catch (Exception e) { From 257edd40ff204499c4971156076f9b40aaf34d49 Mon Sep 17 00:00:00 2001 From: Adir Date: Thu, 7 Oct 2021 14:03:31 +0300 Subject: [PATCH 05/15] Changed CompositionBackdropCornerRadius to be nullable --- src/Windows/Avalonia.Win32/Win32Platform.cs | 5 +++-- .../WinRT/Composition/WinUICompositorConnection.cs | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 9d56306c59..c84ccde653 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -92,9 +92,10 @@ namespace Avalonia /// /// When enabled, create rounded corner blur brushes - /// If set to zero the brushes will be created using default settings (sharp corners) + /// If set to null the brushes will be created using default settings (sharp corners) + /// This can be useful when you need a rounded-corner blurred Windows 10 app, or borderless Windows 11 app /// - public float CompositionBackdropCornerRadius { get; set; } + public float? CompositionBackdropCornerRadius { get; set; } } } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index 21de6f169b..57b0f71306 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -17,7 +17,9 @@ namespace Avalonia.Win32.WinRT.Composition { class WinUICompositorConnection : IRenderTimer { + private readonly float? _backdropCornerRadius; private readonly EglContext _syncContext; + private readonly ICompositionBrush _micaBrush; private ICompositor _compositor; private ICompositor2 _compositor2; private ICompositor5 _compositor5; @@ -27,11 +29,9 @@ namespace Avalonia.Win32.WinRT.Composition private EglPlatformOpenGlInterface _gl; private ICompositorDesktopInterop _compositorDesktopInterop; private ICompositionBrush _blurBrush; - private readonly ICompositionBrush _micaBrush; private object _pumpLock = new object(); - private readonly float _backdropCornerRadius; - public WinUICompositorConnection(EglPlatformOpenGlInterface gl, object pumpLock, float backdropCornerRadius) + public WinUICompositorConnection(EglPlatformOpenGlInterface gl, object pumpLock, float? backdropCornerRadius) { _gl = gl; _pumpLock = pumpLock; @@ -52,7 +52,7 @@ namespace Avalonia.Win32.WinRT.Composition public EglPlatformOpenGlInterface Egl => _gl; - static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle, float backdropCornerRadius) + static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle, float? backdropCornerRadius) { var tcs = new TaskCompletionSource(); var pumpLock = new object(); @@ -135,7 +135,7 @@ namespace Avalonia.Win32.WinRT.Composition } public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle, - float backdropCornerRadius) + float? backdropCornerRadius) { const int majorRequired = 10; const int buildRequired = 17134; @@ -247,10 +247,10 @@ namespace Avalonia.Win32.WinRT.Composition private ICompositionRoundedRectangleGeometry ClipVisual(params IVisual[] containerVisuals) { - if (_backdropCornerRadius == 0) + if (!_backdropCornerRadius.HasValue) return null; using var roundedRectangleGeometry = _compositor5.CreateRoundedRectangleGeometry(); - roundedRectangleGeometry.SetCornerRadius(new Vector2(_backdropCornerRadius, _backdropCornerRadius)); + roundedRectangleGeometry.SetCornerRadius(new Vector2(_backdropCornerRadius.Value, _backdropCornerRadius.Value)); using var compositor6 = _compositor.QueryInterface(); using var compositionGeometry = roundedRectangleGeometry From 74c72f7aee00a6a616dd01b5b719af6b8e8459be Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 14 Oct 2021 18:00:23 +0300 Subject: [PATCH 06/15] [X11] Check for XOpenDisplay error _before_ trying to use display --- src/Avalonia.X11/X11Platform.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 5d80c860a7..d3aeefd088 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -52,11 +52,14 @@ namespace Avalonia.X11 XInitThreads(); Display = XOpenDisplay(IntPtr.Zero); + if (Display == IntPtr.Zero) + throw new Exception("XOpenDisplay failed"); DeferredDisplay = XOpenDisplay(IntPtr.Zero); + if (DeferredDisplay == IntPtr.Zero) + throw new Exception("XOpenDisplay failed"); + OrphanedWindow = XCreateSimpleWindow(Display, XDefaultRootWindow(Display), 0, 0, 1, 1, 0, IntPtr.Zero, IntPtr.Zero); - if (Display == IntPtr.Zero) - throw new Exception("XOpenDisplay failed"); XError.Init(); Info = new X11Info(Display, DeferredDisplay, useXim); From b4738926493db56ab99eb96b361a6b2c55487300 Mon Sep 17 00:00:00 2001 From: 0x90d <46010672+0x90d@users.noreply.github.com> Date: Fri, 15 Oct 2021 05:29:42 +0200 Subject: [PATCH 07/15] Update DataGridColumn.cs --- .../DataGridColumn.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 4ab2869138..07adac597f 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -653,6 +653,26 @@ namespace Avalonia.Controls return null; } + /// + /// Switches the current state of sort direction + /// + /// Clear the current sort direction instead + public void PerformSort(bool clear) + { + //InvokeProcessSort is already validating if sorting is possible + _headerCell?.InvokeProcessSort(clear ? Input.KeyModifiers.Control : Input.KeyModifiers.None); + } + + /// + /// Changes the sort direction of this column + /// + /// New sort direction + public void PerformSort(ListSortDirection direction) + { + //InvokeProcessSort is already validating if sorting is possible + _headerCell?.InvokeProcessSort(Input.KeyModifiers.None, direction); + } + /// /// When overridden in a derived class, causes the column cell being edited to revert to the unedited value. /// From 68a3f7fa975617fac4134b576b147fc028fede3d Mon Sep 17 00:00:00 2001 From: 0x90d <46010672+0x90d@users.noreply.github.com> Date: Fri, 15 Oct 2021 05:35:19 +0200 Subject: [PATCH 08/15] Update DataGridColumnHeader.cs --- .../DataGridColumnHeader.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 6f957497cb..85fd55800a 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -201,21 +201,21 @@ namespace Avalonia.Controls handled = true; } - internal void InvokeProcessSort(KeyModifiers keyModifiers) + internal void InvokeProcessSort(KeyModifiers keyModifiers, ListSortDirection? forcedDirection = null) { Debug.Assert(OwningGrid != null); - if (OwningGrid.WaitForLostFocus(() => InvokeProcessSort(keyModifiers))) + if (OwningGrid.WaitForLostFocus(() => InvokeProcessSort(keyModifiers, forcedDirection))) { return; } if (OwningGrid.CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true)) { - Avalonia.Threading.Dispatcher.UIThread.Post(() => ProcessSort(keyModifiers)); + Avalonia.Threading.Dispatcher.UIThread.Post(() => ProcessSort(keyModifiers, forcedDirection)); } } //TODO GroupSorting - internal void ProcessSort(KeyModifiers keyModifiers) + internal void ProcessSort(KeyModifiers keyModifiers, ListSortDirection? forcedDirection = null) { // if we can sort: // - AllowUserToSortColumns and CanSort are true, and @@ -259,7 +259,14 @@ namespace Avalonia.Controls { if (sort != null) { - newSort = sort.SwitchSortDirection(); + if (forcedDirection == null || sort.Direction != forcedDirection) + { + newSort = sort.SwitchSortDirection(); + } + else + { + newSort = sort; + } // changing direction should not affect sort order, so we replace this column's // sort description instead of just adding it to the end of the collection @@ -276,7 +283,10 @@ namespace Avalonia.Controls } else if (OwningColumn.CustomSortComparer != null) { - newSort = DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer); + newSort = forcedDirection != null ? + DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer, forcedDirection.Value) : + DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer); + owningGrid.DataConnection.SortDescriptions.Add(newSort); } @@ -290,6 +300,10 @@ namespace Avalonia.Controls } newSort = DataGridSortDescription.FromPath(propertyName, culture: collectionView.Culture); + if (forcedDirection != null && newSort.Direction != forcedDirection) + { + newSort = newSort.SwitchSortDirection(); + } owningGrid.DataConnection.SortDescriptions.Add(newSort); } From d138924a95527bc55908a3b7374e9ec1517797d9 Mon Sep 17 00:00:00 2001 From: 0x90d <46010672+0x90d@users.noreply.github.com> Date: Fri, 15 Oct 2021 05:35:55 +0200 Subject: [PATCH 09/15] Update DataGridColumn.cs --- src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 07adac597f..7ad06c194d 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -657,10 +657,10 @@ namespace Avalonia.Controls /// Switches the current state of sort direction /// /// Clear the current sort direction instead - public void PerformSort(bool clear) + public void PerformSort(bool? clear) { //InvokeProcessSort is already validating if sorting is possible - _headerCell?.InvokeProcessSort(clear ? Input.KeyModifiers.Control : Input.KeyModifiers.None); + _headerCell?.InvokeProcessSort(clear == true ? Input.KeyModifiers.Control : Input.KeyModifiers.None); } /// From e564a01663c1d538329f2052c1718063d696fc01 Mon Sep 17 00:00:00 2001 From: 0x90d <46010672+0x90d@users.noreply.github.com> Date: Fri, 15 Oct 2021 06:36:07 +0200 Subject: [PATCH 10/15] Update DataGridColumn.cs --- src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 7ad06c194d..f275d7cc94 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -653,21 +653,29 @@ namespace Avalonia.Controls return null; } + /// + /// Clears the current sort direction + /// + public void ClearSort() + { + //InvokeProcessSort is already validating if sorting is possible + _headerCell?.InvokeProcessSort(Input.KeyModifiers.Control); + } + /// /// Switches the current state of sort direction /// - /// Clear the current sort direction instead - public void PerformSort(bool? clear) + public void Sort() { //InvokeProcessSort is already validating if sorting is possible - _headerCell?.InvokeProcessSort(clear == true ? Input.KeyModifiers.Control : Input.KeyModifiers.None); + _headerCell?.InvokeProcessSort(Input.KeyModifiers.None); } /// /// Changes the sort direction of this column /// /// New sort direction - public void PerformSort(ListSortDirection direction) + public void Sort(ListSortDirection direction) { //InvokeProcessSort is already validating if sorting is possible _headerCell?.InvokeProcessSort(Input.KeyModifiers.None, direction); From f6406cecf0d4f39d829f2fb192eff03ade26b50f Mon Sep 17 00:00:00 2001 From: 0x90d <46010672+0x90d@users.noreply.github.com> Date: Fri, 15 Oct 2021 06:47:09 +0200 Subject: [PATCH 11/15] Update DataGridColumn.cs --- src/Avalonia.Controls.DataGrid/DataGridColumn.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index f275d7cc94..5499257171 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -9,6 +9,7 @@ using Avalonia.VisualTree; using Avalonia.Collections; using Avalonia.Utilities; using System; +using System.ComponentModel; using System.Linq; using System.Diagnostics; using Avalonia.Controls.Utils; From 4e65b0296b9113e56d54b63577e6c56ce28860e6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Oct 2021 09:09:24 +0200 Subject: [PATCH 12/15] Added failing tests for #6729. --- .../ItemsRepeaterTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/Avalonia.Controls.UnitTests/ItemsRepeaterTests.cs diff --git a/tests/Avalonia.Controls.UnitTests/ItemsRepeaterTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsRepeaterTests.cs new file mode 100644 index 0000000000..321676abc0 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/ItemsRepeaterTests.cs @@ -0,0 +1,24 @@ +using System.Collections.ObjectModel; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class ItemsRepeaterTests + { + [Fact] + public void Can_Reassign_Items() + { + var target = new ItemsRepeater(); + target.Items = new ObservableCollection(); + target.Items = new ObservableCollection(); + } + + [Fact] + public void Can_Reassign_Items_To_Null() + { + var target = new ItemsRepeater(); + target.Items = new ObservableCollection(); + target.Items = null; + } + } +} From e2a8b56aad64a5da1962331648c75871eb111b96 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Oct 2021 09:18:12 +0200 Subject: [PATCH 13/15] Unsubscribe from ItemsSourceView before disposing it. --- src/Avalonia.Controls/Repeater/ItemsRepeater.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 01200e87e3..0ff8fcbd28 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -588,14 +588,14 @@ namespace Avalonia.Controls throw new AvaloniaInternalException("Cannot set ItemsSourceView during layout."); } - ItemsSourceView?.Dispose(); - ItemsSourceView = newValue; - if (oldValue != null) { oldValue.CollectionChanged -= OnItemsSourceViewChanged; } + ItemsSourceView?.Dispose(); + ItemsSourceView = newValue; + if (newValue != null) { newValue.CollectionChanged += OnItemsSourceViewChanged; From 8322d4f428ee0b682f8663ac6157865b0a51e93e Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Fri, 15 Oct 2021 12:13:58 +0300 Subject: [PATCH 14/15] fix --- src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs index ea5dcdeeba..526be6e0f0 100644 --- a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs +++ b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs @@ -7,7 +7,7 @@ Mica } - public interface IBlurHost + internal interface IBlurHost { void SetBlur(BlurEffect enable); } From 2d14a049d7089d2cff8f3b003ca3b5bc6e4446e3 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Fri, 15 Oct 2021 13:05:07 +0300 Subject: [PATCH 15/15] add ctor to DrawingImage which accepts Drawing --- src/Avalonia.Visuals/Media/DrawingImage.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Avalonia.Visuals/Media/DrawingImage.cs b/src/Avalonia.Visuals/Media/DrawingImage.cs index 56c883014a..6fa8d397a5 100644 --- a/src/Avalonia.Visuals/Media/DrawingImage.cs +++ b/src/Avalonia.Visuals/Media/DrawingImage.cs @@ -11,6 +11,14 @@ namespace Avalonia.Media /// public class DrawingImage : AvaloniaObject, IImage, IAffectsRender { + public DrawingImage() + { + } + + public DrawingImage(Drawing drawing) + { + Drawing = drawing; + } /// /// Defines the property. ///