diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
index a475cf5eac..9269dc70f8 100644
--- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs
+++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
@@ -62,7 +62,7 @@ namespace Avalonia.ReactiveUI
/// for the property.
///
public static readonly StyledProperty ViewContractProperty =
- AvaloniaProperty.Register(nameof(ViewContract));
+ AvaloniaProperty.Register(nameof(ViewContract));
///
/// Initializes a new instance of the class.
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index f7838c9f46..9d53691597 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -765,6 +765,14 @@ namespace Avalonia.Win32.Interop
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
+ DWMWA_PASSIVE_UPDATE_MODE,
+ DWMWA_USE_HOSTBACKDROPBRUSH,
+ DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
+ DWMWA_WINDOW_CORNER_PREFERENCE = 33,
+ DWMWA_BORDER_COLOR,
+ DWMWA_CAPTION_COLOR,
+ DWMWA_TEXT_COLOR,
+ DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
DWMWA_LAST
};
@@ -1456,6 +1464,9 @@ namespace Avalonia.Win32.Interop
[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute);
+ [DllImport("dwmapi.dll")]
+ public static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, void* pvAttribute, int cbAttribute);
+
[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(out bool enabled);
diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
index 57b0f71306..76af12e8ca 100644
--- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
+++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs
@@ -17,11 +17,11 @@ namespace Avalonia.Win32.WinRT.Composition
{
class WinUICompositorConnection : IRenderTimer
{
+ public static readonly Version MinHostBackdropVersion = new Version(10, 0, 22000);
private readonly float? _backdropCornerRadius;
private readonly EglContext _syncContext;
private readonly ICompositionBrush _micaBrush;
private ICompositor _compositor;
- private ICompositor2 _compositor2;
private ICompositor5 _compositor5;
private ICompositorInterop _compositorInterop;
private AngleWin32EglDisplay _angle;
@@ -39,12 +39,11 @@ namespace Avalonia.Win32.WinRT.Composition
_syncContext = _gl.PrimaryEglContext;
_angle = (AngleWin32EglDisplay)_gl.Display;
_compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor");
- _compositor2 = _compositor.QueryInterface();
_compositor5 = _compositor.QueryInterface();
_compositorInterop = _compositor.QueryInterface();
_compositorDesktopInterop = _compositor.QueryInterface();
using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true);
-
+
_device = _compositorInterop.CreateGraphicsDevice(device);
_blurBrush = CreateAcrylicBlurBackdropBrush();
_micaBrush = CreateMicaBackdropBrush();
@@ -71,7 +70,7 @@ namespace Avalonia.Win32.WinRT.Composition
AvaloniaLocator.CurrentMutable.BindToSelf(connect);
AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect);
tcs.SetResult(true);
-
+
}
catch (Exception e)
{
@@ -99,7 +98,7 @@ namespace Avalonia.Win32.WinRT.Composition
}
public void Dispose()
{
-
+
}
public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus)
@@ -118,12 +117,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)
{
@@ -172,12 +171,12 @@ 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,
DirectXAlphaMode.Premultiplied);
using var surface = drawingSurface.QueryInterface();
using var surfaceInterop = drawingSurface.QueryInterface();
-
+
using var surfaceBrush = _compositor.CreateSurfaceBrushWithSurface(surface);
using var brush = surfaceBrush.QueryInterface();
@@ -190,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);
@@ -202,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);
}
@@ -234,10 +233,8 @@ namespace Avalonia.Win32.WinRT.Composition
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();
-
-
+ using var backdropBrush = CreateBackdropBrush();
+
var saturateEffect = new SaturationEffect(blurEffect);
using var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect);
using var sat = satEffectFactory.CreateBrush();
@@ -261,8 +258,8 @@ namespace Avalonia.Win32.WinRT.Composition
foreach (var visual in containerVisuals)
{
visual?.SetClip(geometricClipWithGeometry.QueryInterface());
- }
-
+ }
+
return roundedRectangleGeometry.CloneReference();
}
@@ -271,8 +268,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));
@@ -280,6 +277,29 @@ namespace Avalonia.Win32.WinRT.Composition
return visual.CloneReference();
}
+ private ICompositionBrush CreateBackdropBrush()
+ {
+ ICompositionBackdropBrush brush = null;
+ try
+ {
+ if (Win32Platform.WindowsVersion >= MinHostBackdropVersion)
+ {
+ using var compositor3 = _compositor.QueryInterface();
+ brush = compositor3.CreateHostBackdropBrush();
+ }
+ else
+ {
+ using var compositor2 = _compositor.QueryInterface();
+ brush = compositor2.CreateBackdropBrush();
+ }
+
+ return brush.QueryInterface();
+ }
+ finally
+ {
+ brush?.Dispose();
+ }
+ }
public event Action Tick;
}
diff --git a/src/Windows/Avalonia.Win32/WinRT/winrt.idl b/src/Windows/Avalonia.Win32/WinRT/winrt.idl
index 18a9a26fca..851df9dae6 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(C9DD8EF0-6EB1-4E3C-A658-675D9C64D4AB)]
+interface ICompositor3 : IInspectable
+{
+ HRESULT CreateHostBackdropBrush([out][retval] ICompositionBackdropBrush** result);
+}
+
[uuid(0D8FB190-F122-5B8D-9FDD-543B0D8EB7F3)]
interface ICompositorWithBlurredWallpaperBackdropBrush : IInspectable
{
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index aae65a792b..3953a0995d 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -86,7 +86,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;
@@ -174,7 +174,7 @@ namespace Avalonia.Win32
public Action PositionChanged { get; set; }
public Action WindowStateChanged { get; set; }
-
+
public Action LostFocus { get; set; }
public Action TransparencyLevelChanged { get; set; }
@@ -247,7 +247,7 @@ namespace Avalonia.Win32
{
get
{
- if(_isFullScreenActive)
+ if (_isFullScreenActive)
{
return WindowState.FullScreen;
}
@@ -270,7 +270,7 @@ namespace Avalonia.Win32
ShowWindow(value, value != WindowState.Minimized); // If the window is minimized, it shouldn't be activated
}
- _showWindowState = value;
+ _showWindowState = value;
}
}
@@ -278,7 +278,7 @@ namespace Avalonia.Win32
protected IntPtr Hwnd => _hwnd;
- public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel)
+ public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{
TransparencyLevel = EnableBlur(transparencyLevel);
}
@@ -318,12 +318,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)
@@ -379,13 +379,24 @@ namespace Avalonia.Win32
{
if (_isUsingComposition)
{
- _blurHost?.SetBlur(transparencyLevel switch
+ var effect = transparencyLevel switch
{
WindowTransparencyLevel.Mica => BlurEffect.Mica,
WindowTransparencyLevel.AcrylicBlur => BlurEffect.Acrylic,
WindowTransparencyLevel.Blur => BlurEffect.Acrylic,
_ => BlurEffect.None
- });
+ };
+
+ if (Win32Platform.WindowsVersion >= WinUICompositorConnection.MinHostBackdropVersion)
+ {
+ unsafe
+ {
+ int pvUseBackdropBrush = effect == BlurEffect.Acrylic ? 1 : 0;
+ DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int));
+ }
+ }
+
+ _blurHost?.SetBlur(effect);
return transparencyLevel;
}
@@ -483,12 +494,12 @@ namespace Avalonia.Win32
if (customRendererFactory != null)
return customRendererFactory.Create(root, loop);
- return Win32Platform.UseDeferredRendering
- ? _isUsingComposition
+ return Win32Platform.UseDeferredRendering
+ ? _isUsingComposition
? new DeferredRenderer(root, loop)
{
RenderOnlyOnRenderThread = true
- }
+ }
: (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock)
: new ImmediateRenderer(root);
}
@@ -543,7 +554,7 @@ namespace Avalonia.Win32
{
BeforeCloseCleanup(true);
}
-
+
DestroyWindow(_hwnd);
_hwnd = IntPtr.Zero;
}
@@ -609,7 +620,7 @@ namespace Avalonia.Win32
public void SetParent(IWindowImpl parent)
{
_parent = (WindowImpl)parent;
-
+
var parentHwnd = _parent?._hwnd ?? IntPtr.Zero;
if (parentHwnd == IntPtr.Zero && !_windowProperties.ShowInTaskbar)
@@ -723,7 +734,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,
@@ -779,7 +790,7 @@ namespace Avalonia.Win32
}
if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
- {
+ {
var monitor = MonitorFromWindow(
_hwnd,
MONITOR.MONITOR_DEFAULTTONEAREST);
@@ -862,14 +873,14 @@ namespace Avalonia.Win32
}
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);
@@ -892,7 +903,7 @@ 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;
@@ -917,7 +928,7 @@ namespace Avalonia.Win32
{
return;
}
-
+
if (DwmIsCompositionEnabled(out bool compositionEnabled) < 0 || !compositionEnabled)
{
_isClientAreaExtended = false;
@@ -945,11 +956,11 @@ 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) &&
+ if (!_isClientAreaExtended || (_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.SystemChrome) &&
!_extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome)))
{
EnableCloseButton(_hwnd);
@@ -965,12 +976,12 @@ namespace Avalonia.Win32
private void ShowWindow(WindowState state, bool activate)
{
_shown = true;
-
+
if (_isClientAreaExtended)
{
ExtendClientArea();
}
-
+
ShowWindowCommand? command;
var newWindowProperties = _windowProperties;
@@ -988,7 +999,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;
@@ -1019,7 +1030,7 @@ namespace Avalonia.Win32
SetForegroundWindow(_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
@@ -1037,7 +1048,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)
{
@@ -1064,7 +1075,7 @@ namespace Avalonia.Win32
SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
}
}
- }
+ }
private WindowStyles GetWindowStateStyles()
{
@@ -1241,7 +1252,7 @@ namespace Avalonia.Win32
SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE |
SetWindowPosFlags.SWP_FRAMECHANGED);
}
- }
+ }
}
private const int MF_BYCOMMAND = 0x0;
@@ -1291,9 +1302,9 @@ namespace Avalonia.Win32
public void SetExtendClientAreaToDecorationsHint(bool hint)
{
_isClientAreaExtended = hint;
-
- ExtendClientArea();
- }
+
+ ExtendClientArea();
+ }
public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints)
{
@@ -1301,7 +1312,7 @@ namespace Avalonia.Win32
ExtendClientArea();
}
-
+
///
public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
{
@@ -1315,7 +1326,7 @@ namespace Avalonia.Win32
///
public Action ExtendClientAreaToDecorationsChanged { get; set; }
-
+
///
public bool NeedsManagedDecorations => _isClientAreaExtended && _extendChromeHints.HasAllFlags(ExtendClientAreaChromeHints.PreferSystemChrome);
@@ -1354,7 +1365,7 @@ namespace Avalonia.Win32
{
private readonly WindowImpl _owner;
private readonly PlatformResizeReason _restore;
-
+
public ResizeReasonScope(WindowImpl owner, PlatformResizeReason restore)
{
_owner = owner;