diff --git a/src/Modern.WindowKit/Avalonia.Mac/AvaloniaNativePlatform.cs b/src/Modern.WindowKit/Avalonia.Mac/AvaloniaNativePlatform.cs index d7f4d86..9a951a6 100644 --- a/src/Modern.WindowKit/Avalonia.Mac/AvaloniaNativePlatform.cs +++ b/src/Modern.WindowKit/Avalonia.Mac/AvaloniaNativePlatform.cs @@ -108,23 +108,18 @@ void DoInitialize(AvaloniaNativePlatformOptions options) // .Bind().ToConstant(this) // .Bind().ToConstant(new ClipboardImpl(_factory.CreateClipboard())) // .Bind().ToConstant(new DefaultRenderTimer(60)) - // .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta, wholeWordTextActionModifiers: KeyModifiers.Alt)) // .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()) // .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)) // .Bind().ToConstant(applicationPlatform) // .Bind().ToConstant(new MacOSNativeMenuCommands(_factory.CreateApplicationCommands())); - //var renderLoop = new RenderLoop(); - //AvaloniaLocator.CurrentMutable.Bind().ToConstant(renderLoop); + //var hotkeys = new PlatformHotkeyConfiguration(KeyModifiers.Meta, wholeWordTextActionModifiers: KeyModifiers.Alt); + //hotkeys.MoveCursorToTheStartOfLine.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers)); + //hotkeys.MoveCursorToTheStartOfLineWithSelection.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); + //hotkeys.MoveCursorToTheEndOfLine.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers)); + //hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); - //var hotkeys = AvaloniaLocator.Current.GetService(); - //if (hotkeys is not null) - //{ - // hotkeys.MoveCursorToTheStartOfLine.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers)); - // hotkeys.MoveCursorToTheStartOfLineWithSelection.Add(new KeyGesture(Key.Left, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); - // hotkeys.MoveCursorToTheEndOfLine.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers)); - // hotkeys.MoveCursorToTheEndOfLineWithSelection.Add(new KeyGesture(Key.Right, hotkeys.CommandModifiers | hotkeys.SelectionModifiers)); - //} + //AvaloniaLocator.CurrentMutable.Bind().ToConstant(hotkeys); //if (_options.UseGpu) //{ @@ -140,8 +135,8 @@ void DoInitialize(AvaloniaNativePlatformOptions options) // // ignored // } //} - - //Compositor = new Compositor(renderLoop, _platformGl); + + //Compositor = new Compositor(_platformGl, true); } //public ITrayIconImpl CreateTrayIcon() diff --git a/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs b/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs index efcf668..4ff27db 100644 --- a/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs +++ b/src/Modern.WindowKit/Avalonia.Mac/WindowImplBase.cs @@ -65,6 +65,7 @@ internal abstract partial class WindowBaseImpl : IWindowBaseImpl, //private NativeControlHostImpl _nativeControlHost; private IStorageProvider _storageProvider; //private PlatformBehaviorInhibition _platformBehaviorInhibition; + private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None; internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) //AvaloniaNativeGlPlatformGraphics glFeature) @@ -200,7 +201,7 @@ void IAvnWindowBaseEvents.Closed() void IAvnWindowBaseEvents.Paint() { - Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); + Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); var s = _parent.ClientSize; _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height)); } @@ -243,7 +244,7 @@ void IAvnWindowBaseEvents.ScalingChanged(double scaling) void IAvnWindowBaseEvents.RunRenderPriorityJobs() { - Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); + Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); } void IAvnWindowBaseEvents.LostFocus() @@ -365,13 +366,7 @@ public void Resize(Size clientSize, WindowResizeReason reason) _native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason); } - //public IRenderer CreateRenderer(IRenderRoot root) - //{ - // return new CompositingRenderer(root, AvaloniaNativePlatform.Compositor, () => Surfaces) - // { - // RenderOnlyOnRenderThread = false - // }; - //} + //public Compositor Compositor => AvaloniaNativePlatform.Compositor; public virtual void Dispose() { @@ -485,26 +480,47 @@ internal void BeginDraggingSession(AvnDragDropEffects effects, AvnPoint point, I _native?.BeginDragAndDropOperation(effects, point, clipboard, callback, sourceHandle); } - public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) + public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) { - if (TransparencyLevel != transparencyLevel) + foreach (var level in transparencyLevels) { - if (transparencyLevel > WindowTransparencyLevel.Transparent) - transparencyLevel = WindowTransparencyLevel.AcrylicBlur; + AvnWindowTransparencyMode? mode = null; - TransparencyLevel = transparencyLevel; + if (level == WindowTransparencyLevel.None) + mode = AvnWindowTransparencyMode.Opaque; + if (level == WindowTransparencyLevel.Transparent) + mode = AvnWindowTransparencyMode.Transparent; + else if (level == WindowTransparencyLevel.AcrylicBlur) + mode = AvnWindowTransparencyMode.Blur; - _native.SetTransparencyMode(transparencyLevel == WindowTransparencyLevel.None - ? AvnWindowTransparencyMode.Opaque - : transparencyLevel == WindowTransparencyLevel.Transparent - ? AvnWindowTransparencyMode.Transparent - : AvnWindowTransparencyMode.Blur); + if (mode.HasValue && level != TransparencyLevel) + { + _native?.SetTransparencyMode(mode.Value); + TransparencyLevel = level; + return; + } + } - TransparencyLevelChanged?.Invoke(TransparencyLevel); - } + // If we get here, we didn't find a supported level. Use the default of None. + if (TransparencyLevel != WindowTransparencyLevel.None) + { + _native?.SetTransparencyMode(AvnWindowTransparencyMode.Opaque); + TransparencyLevel = WindowTransparencyLevel.None; + } } - public WindowTransparencyLevel TransparencyLevel { get; private set; } = WindowTransparencyLevel.None; + public WindowTransparencyLevel TransparencyLevel + { + get => _transparencyLevel; + private set + { + if (_transparencyLevel != value) + { + _transparencyLevel = value; + TransparencyLevelChanged?.Invoke(value); + } + } + } public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { diff --git a/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs index d8b22fe..c09f0cc 100644 --- a/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Modern.WindowKit/Avalonia.Skia/SkiaSharpExtensions.cs @@ -13,6 +13,7 @@ public static SKFilterQuality ToSKFilterQuality(this BitmapInterpolationMode int { switch (interpolationMode) { + case BitmapInterpolationMode.Unspecified: case BitmapInterpolationMode.LowQuality: return SKFilterQuality.Low; case BitmapInterpolationMode.MediumQuality: @@ -20,7 +21,6 @@ public static SKFilterQuality ToSKFilterQuality(this BitmapInterpolationMode int case BitmapInterpolationMode.HighQuality: return SKFilterQuality.High; case BitmapInterpolationMode.None: - case BitmapInterpolationMode.Unspecified: return SKFilterQuality.None; default: throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null); diff --git a/src/Modern.WindowKit/Avalonia.Win32/PlatformConstants.cs b/src/Modern.WindowKit/Avalonia.Win32/PlatformConstants.cs index 3ed47e8..044afbb 100644 --- a/src/Modern.WindowKit/Avalonia.Win32/PlatformConstants.cs +++ b/src/Modern.WindowKit/Avalonia.Win32/PlatformConstants.cs @@ -2,12 +2,13 @@ namespace Modern.WindowKit.Win32 { - public static class PlatformConstants + internal static class PlatformConstants { public const string WindowHandleType = "HWND"; public const string CursorHandleType = "HCURSOR"; - internal static readonly Version Windows8 = new Version(6, 2); - internal static readonly Version Windows7 = new Version(6, 1); + public static readonly Version Windows10 = new Version(10, 0); + public static readonly Version Windows8 = new Version(6, 2); + public static readonly Version Windows7 = new Version(6, 1); } } diff --git a/src/Modern.WindowKit/Avalonia.Win32/Win32Platform.cs b/src/Modern.WindowKit/Avalonia.Win32/Win32Platform.cs index 38ca4c9..3036e68 100644 --- a/src/Modern.WindowKit/Avalonia.Win32/Win32Platform.cs +++ b/src/Modern.WindowKit/Avalonia.Win32/Win32Platform.cs @@ -159,11 +159,10 @@ public static void Initialize(Win32PlatformOptions options) // .Bind().ToConstant(WindowsKeyboardDevice.Instance) // .Bind().ToSingleton() // .Bind().ToConstant(s_instance._dispatcher) - // .Bind().ToConstant(new RenderLoop()) // .Bind().ToConstant(renderTimer) // .Bind().ToConstant(s_instance) // .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control) - // { + // { // OpenContextMenu = // { // // Add Shift+F10 @@ -181,7 +180,7 @@ public static void Initialize(Win32PlatformOptions options) //if (OleContext.Current != null) // AvaloniaLocator.CurrentMutable.Bind().ToSingleton(); - //s_compositor = new Compositor(AvaloniaGlobals.GetRequiredService(), platformGraphics); + //s_compositor = new Compositor( platformGraphics); } public event EventHandler? ShutdownRequested; diff --git a/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.AppWndProc.cs index b52fd81..1977433 100644 --- a/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -594,7 +594,6 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam, case WindowsMessage.WM_PAINT: { //using (NonPumpingSyncContext.Use(NonPumpingWaitHelperImpl.Instance)) - //using (_rendererLock.Lock()) //{ if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) { @@ -616,12 +615,6 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam, case WindowsMessage.WM_SIZE: { - //using (NonPumpingSyncContext.Use(NonPumpingWaitHelperImpl.Instance)) - //using (_rendererLock.Lock()) - //{ - // // Do nothing here, just block until the pending frame render is completed on the render thread - //} - var size = (SizeCommand)wParam; if (Resized != null && @@ -838,10 +831,7 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam, } } - //using (_rendererLock.Lock()) - // { - return DefWindowProc(hWnd, msg, wParam, lParam); - //} + return DefWindowProc(hWnd, msg, wParam, lParam); } //private Lazy?>? CreateLazyIntermediatePoints(POINTER_INFO info) diff --git a/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.cs b/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.cs index aee2682..46c861e 100644 --- a/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.cs +++ b/src/Modern.WindowKit/Avalonia.Win32/WindowImpl.cs @@ -25,6 +25,7 @@ using Modern.WindowKit.Win32.WinRT; using static Modern.WindowKit.Win32.Interop.UnmanagedMethods; using Modern.WindowKit.Input.Platform; +using System.Diagnostics; namespace Modern.WindowKit.Win32 { @@ -70,7 +71,6 @@ internal partial class WindowImpl : IWindowImpl //, EglGlPlatformSurface.IEglWin private readonly TouchDevice _touchDevice; private readonly WindowsMouseDevice _mouseDevice; //private readonly PenDevice _penDevice; - //private readonly ManagedDeferredRendererLock _rendererLock; private readonly FramebufferManager _framebuffer; private readonly object? _gl; private readonly bool _wmPointerEnabled; @@ -98,6 +98,7 @@ internal partial class WindowImpl : IWindowImpl //, EglGlPlatformSurface.IEglWin private bool _hiddenWindowIsParent; private uint _langid; private bool _ignoreWmChar; + private WindowTransparencyLevel _transparencyLevel; //private const int MaxPointerHistorySize = 512; //private static readonly PooledList s_intermediatePointsPooledList = new(); @@ -128,7 +129,6 @@ public WindowImpl() IsResizable = true, Decorations = SystemDecorations.Full }; - //_rendererLock = new ManagedDeferredRendererLock(); //var glPlatform = AvaloniaLocator.Current.GetService(); @@ -182,6 +182,7 @@ public WindowImpl() _storageProvider = new Win32StorageProvider(this); //_nativeControlHost = new Win32NativeControlHost(this, _isUsingComposition); + _transparencyLevel = _isUsingComposition ? WindowTransparencyLevel.Transparent : WindowTransparencyLevel.None; s_instances.Add(this); } @@ -315,7 +316,18 @@ public WindowState WindowState } } - public WindowTransparencyLevel TransparencyLevel { get; private set; } + public WindowTransparencyLevel TransparencyLevel + { + get => _transparencyLevel; + private set + { + if (_transparencyLevel != value) + { + _transparencyLevel = value; + TransparencyLevelChanged?.Invoke(value); + } + } + } protected IntPtr Hwnd => _hwnd; @@ -346,170 +358,132 @@ public WindowState WindowState return null; } - public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) + public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) { - TransparencyLevel = EnableBlur(transparencyLevel); - } + var windowsVersion = Win32Platform.WindowsVersion; + + foreach (var level in transparencyLevels) + { + if (!IsSupported(level, windowsVersion)) + continue; + if (level == TransparencyLevel) + return; + if (level == WindowTransparencyLevel.Transparent) + SetTransparencyTransparent(windowsVersion); + else if (level == WindowTransparencyLevel.Blur) + SetTransparencyBlur(windowsVersion); + else if (level == WindowTransparencyLevel.AcrylicBlur) + SetTransparencyAcrylicBlur(windowsVersion); + else if (level == WindowTransparencyLevel.Mica) + SetTransparencyMica(windowsVersion); + + TransparencyLevel = level; + return; + } - private WindowTransparencyLevel EnableBlur(WindowTransparencyLevel transparencyLevel) - { - if (Win32Platform.WindowsVersion.Major >= 6) + // If we get here, we didn't find a supported level. Use the defualt of Transparent or + // None, depending on whether composition is enabled. + if (_isUsingComposition) { - if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled) - { - return WindowTransparencyLevel.None; - } - else if (Win32Platform.WindowsVersion.Major >= 10) - { - return Win10EnableBlur(transparencyLevel); - } - else if (Win32Platform.WindowsVersion.Minor >= 2) - { - return Win8xEnableBlur(transparencyLevel); - } - else - { - return Win7EnableBlur(transparencyLevel); - } + SetTransparencyTransparent(windowsVersion); + TransparencyLevel = WindowTransparencyLevel.Transparent; } else { - return WindowTransparencyLevel.None; + TransparencyLevel = WindowTransparencyLevel.None; } } - private WindowTransparencyLevel Win7EnableBlur(WindowTransparencyLevel transparencyLevel) + private bool IsSupported(WindowTransparencyLevel level, Version windowsVersion) { - if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur) - { - transparencyLevel = WindowTransparencyLevel.Blur; - } + // Only None is suppported when composition is disabled. + if (!_isUsingComposition) + return level == WindowTransparencyLevel.None; - var blurInfo = new DWM_BLURBEHIND(false); + // When composition is enabled, None is not supported because the backing visual always + // has an alpha channel + if (level == WindowTransparencyLevel.None) + return false; - if (transparencyLevel == WindowTransparencyLevel.Blur) - { - blurInfo = new DWM_BLURBEHIND(true); - } + // Transparent only supported on Windows 8+. + if (level == WindowTransparencyLevel.Transparent) + return windowsVersion >= PlatformConstants.Windows8; - DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); + // Blur only supported on Windows 8 and lower. + if (level == WindowTransparencyLevel.Blur) + return windowsVersion < PlatformConstants.Windows10; - if (transparencyLevel == WindowTransparencyLevel.Transparent) - { - return WindowTransparencyLevel.None; - } - else - { - return transparencyLevel; - } + //// Acrylic is supported on Windows >= 10.0.15063. + //if (level == WindowTransparencyLevel.AcrylicBlur) + // return windowsVersion >= WinUiCompositionShared.MinAcrylicVersion; + + //// Mica is supported on Windows >= 10.0.22000. + //if (level == WindowTransparencyLevel.Mica) + // return windowsVersion >= WinUiCompositionShared.MinHostBackdropVersion; + + return false; } - private WindowTransparencyLevel Win8xEnableBlur(WindowTransparencyLevel transparencyLevel) + private void SetTransparencyTransparent(Version windowsVersion) { - var accent = new AccentPolicy(); - var accentStructSize = Marshal.SizeOf(); - - if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur) - { - transparencyLevel = WindowTransparencyLevel.Blur; - } + // Transparent only supported with composition on Windows 8+. + if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows8) + return; - if (transparencyLevel == WindowTransparencyLevel.Transparent) - { - accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND; - } - else + if (windowsVersion < PlatformConstants.Windows10) { - accent.AccentState = AccentState.ACCENT_DISABLED; + // Some of the AccentState Enum's values have different meanings on Windows 8.x than on + // Windows 10, hence using ACCENT_ENABLE_BLURBEHIND to disable blurbehind ¯\_(ツ)_/¯. + // Hey, I'm just porting what was here before. + SetAccentState(AccentState.ACCENT_ENABLE_BLURBEHIND); + var blurInfo = new DWM_BLURBEHIND(false); + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); } - 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; - - SetWindowCompositionAttribute(_hwnd, ref data); - - Marshal.FreeHGlobal(accentPtr); + SetUseHostBackdropBrush(false); + _blurHost?.SetBlur(BlurEffect.None); + } - if (transparencyLevel >= WindowTransparencyLevel.Blur) - { - Win7EnableBlur(transparencyLevel); - } + private void SetTransparencyBlur(Version windowsVersion) + { + // Blur only supported with composition on Windows 8 and lower. + if (!_isUsingComposition || windowsVersion >= PlatformConstants.Windows10) + return; - return transparencyLevel; + // Some of the AccentState Enum's values have different meanings on Windows 8.x than on + // Windows 10. + SetAccentState(AccentState.ACCENT_DISABLED); + var blurInfo = new DWM_BLURBEHIND(true); + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); } - private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel) - { - //if (_isUsingComposition) - // { - // var effect = transparencyLevel switch - // { - // WindowTransparencyLevel.Mica => BlurEffect.Mica, - // WindowTransparencyLevel.AcrylicBlur => BlurEffect.Acrylic, - // WindowTransparencyLevel.Blur => BlurEffect.Acrylic, - // _ => BlurEffect.None - // }; - - // if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion) - // { - // unsafe - // { - // int pvUseBackdropBrush = effect == BlurEffect.Acrylic ? 1 : 0; - // DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int)); - // } - // } + private void SetTransparencyAcrylicBlur(Version windowsVersion) + { + // Acrylic blur only supported with composition on Windows >= 10.0.15063. + //if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinAcrylicVersion) + return; - // if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion && effect == BlurEffect.Mica) - // { - // effect = BlurEffect.Acrylic; - // } + SetUseHostBackdropBrush(true); + _blurHost?.SetBlur(BlurEffect.Acrylic); + } - // _blurHost?.SetBlur(effect); + private void SetTransparencyMica(Version windowsVersion) + { + // Mica only supported with composition on Windows >= 10.0.22000. + //if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion) + return; - // return transparencyLevel; - //} - //else - // { - bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628; + SetUseHostBackdropBrush(false); + _blurHost?.SetBlur(BlurEffect.Mica); + } + private void SetAccentState(AccentState state) + { var accent = new AccentPolicy(); - var accentStructSize = Marshal.SizeOf(); - - if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic) - { - transparencyLevel = WindowTransparencyLevel.Blur; - } - - switch (transparencyLevel) - { - default: - case WindowTransparencyLevel.None: - accent.AccentState = AccentState.ACCENT_DISABLED; - break; - - case WindowTransparencyLevel.Transparent: - accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT; - break; + var accentStructSize = Marshal.SizeOf(accent); - case WindowTransparencyLevel.Blur: - accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND; - break; - - case WindowTransparencyLevel.AcrylicBlur: - case WindowTransparencyLevel.ForceAcrylicBlur: // hack-force acrylic. - case WindowTransparencyLevel.Mica: - accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC; - transparencyLevel = WindowTransparencyLevel.AcrylicBlur; - break; - } - - accent.AccentFlags = 2; - accent.GradientColor = 0x01000000; + //Some of the AccentState Enum's values have different meanings on Windows 8.x than on Windows 10 + accent.AccentState = state; var accentPtr = Marshal.AllocHGlobal(accentStructSize); Marshal.StructureToPtr(accent, accentPtr, false); @@ -520,11 +494,19 @@ private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transpar data.Data = accentPtr; SetWindowCompositionAttribute(_hwnd, ref data); - Marshal.FreeHGlobal(accentPtr); + } - return transparencyLevel; - //} + private void SetUseHostBackdropBrush(bool useHostBackdropBrush) + { + //if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion) + return; + + unsafe + { + var pvUseBackdropBrush = useHostBackdropBrush ? 1 : 0; + DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int)); + } } //public IEnumerable Surfaces @@ -585,8 +567,7 @@ public void SetMinMaxSize(Size minSize, Size maxSize) _maxSize = maxSize; } - //public IRenderer CreateRenderer(IRenderRoot root) => - // new CompositingRenderer(root, Win32Platform.Compositor, () => Surfaces); + //public Compositor Compositor => Win32Platform.Compositor; public void Resize(Size value, WindowResizeReason reason) { @@ -1498,7 +1479,7 @@ public ResizeReasonScope(WindowImpl owner, WindowResizeReason restore) public void Dispose() => _owner._resizeReason = _restore; } - private class WindowImplPlatformHandle : IPlatformNativeSurfaceHandle + private class WindowImplPlatformHandle : INativePlatformHandleSurface { private readonly WindowImpl _owner; public WindowImplPlatformHandle(WindowImpl owner) => _owner = owner; diff --git a/src/Modern.WindowKit/Avalonia.Win32/WindowsKeyboardDevice.cs b/src/Modern.WindowKit/Avalonia.Win32/WindowsKeyboardDevice.cs index b8f836b..b51afef 100644 --- a/src/Modern.WindowKit/Avalonia.Win32/WindowsKeyboardDevice.cs +++ b/src/Modern.WindowKit/Avalonia.Win32/WindowsKeyboardDevice.cs @@ -5,7 +5,7 @@ namespace Modern.WindowKit.Win32.Input { - class WindowsKeyboardDevice : KeyboardDevice + internal class WindowsKeyboardDevice : KeyboardDevice { private readonly byte[] _keyStates = new byte[256]; diff --git a/src/Modern.WindowKit/Avalonia.X11/TransparencyHelper.cs b/src/Modern.WindowKit/Avalonia.X11/TransparencyHelper.cs index 8e4a041..d2b788a 100644 --- a/src/Modern.WindowKit/Avalonia.X11/TransparencyHelper.cs +++ b/src/Modern.WindowKit/Avalonia.X11/TransparencyHelper.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; using Modern.WindowKit.Controls; +#nullable enable + namespace Modern.WindowKit.X11 { internal class TransparencyHelper : IDisposable, X11Globals.IGlobalsSubscriber @@ -9,11 +12,23 @@ internal class TransparencyHelper : IDisposable, X11Globals.IGlobalsSubscriber private readonly IntPtr _window; private readonly X11Globals _globals; private WindowTransparencyLevel _currentLevel; - private WindowTransparencyLevel _requestedLevel; + private IReadOnlyList? _requestedLevels; private bool _blurAtomsAreSet; - public Action TransparencyLevelChanged { get; set; } - public WindowTransparencyLevel CurrentLevel => _currentLevel; + public Action? TransparencyLevelChanged { get; set; } + + public WindowTransparencyLevel CurrentLevel + { + get => _currentLevel; + set + { + if (_currentLevel != value) + { + _currentLevel = value; + TransparencyLevelChanged?.Invoke(value); + } + } + } public TransparencyHelper(X11Info x11, IntPtr window, X11Globals globals) { @@ -23,25 +38,53 @@ public TransparencyHelper(X11Info x11, IntPtr window, X11Globals globals) _globals.AddSubscriber(this); } - public void SetTransparencyRequest(WindowTransparencyLevel level) + public void SetTransparencyRequest(IReadOnlyList levels) + { + _requestedLevels = levels; + + foreach (var level in levels) + { + if (!IsSupported(level)) + continue; + + SetBlur(level == WindowTransparencyLevel.Blur); + CurrentLevel = level; + return; + } + + // If we get here, we didn't find a supported level. Use the defualt of Transparent or + // None, depending on whether composition is enabled. + SetBlur(false); + CurrentLevel = _globals.IsCompositionEnabled ? + WindowTransparencyLevel.Transparent : + WindowTransparencyLevel.None; + } + + private bool IsSupported(WindowTransparencyLevel level) { - _requestedLevel = level; - UpdateTransparency(); + // None is suppported when composition is disabled. + if (level == WindowTransparencyLevel.None) + return !_globals.IsCompositionEnabled; + + // Transparent is suppported when composition is enabled. + if (level == WindowTransparencyLevel.Transparent) + return _globals.IsCompositionEnabled; + + // Blur is supported when composition is enabled and KWin is used. + if (level == WindowTransparencyLevel.Blur) + return _globals.IsCompositionEnabled && _globals.WmName == "KWin"; + + return false; } private void UpdateTransparency() { - var newLevel = UpdateAtomsAndGetTransparency(); - if (newLevel != _currentLevel) - { - _currentLevel = newLevel; - TransparencyLevelChanged?.Invoke(newLevel); - } + SetTransparencyRequest(_requestedLevels ?? Array.Empty()); } - - private WindowTransparencyLevel UpdateAtomsAndGetTransparency() + + private void SetBlur(bool blur) { - if (_requestedLevel >= WindowTransparencyLevel.Blur) + if (blur) { if (!_blurAtomsAreSet) { @@ -59,15 +102,7 @@ private WindowTransparencyLevel UpdateAtomsAndGetTransparency() _blurAtomsAreSet = false; } } - - if (!_globals.IsCompositionEnabled) - return WindowTransparencyLevel.None; - if (_requestedLevel >= WindowTransparencyLevel.Blur && CanBlur) - return WindowTransparencyLevel.Blur; - return WindowTransparencyLevel.Transparent; } - - private bool CanBlur => _globals.WmName == "KWin" && _globals.IsCompositionEnabled; public void Dispose() { diff --git a/src/Modern.WindowKit/Avalonia.X11/Utf8Buffer.cs b/src/Modern.WindowKit/Avalonia.X11/Utf8Buffer.cs index fb0cc0b..549b4e3 100644 --- a/src/Modern.WindowKit/Avalonia.X11/Utf8Buffer.cs +++ b/src/Modern.WindowKit/Avalonia.X11/Utf8Buffer.cs @@ -5,7 +5,7 @@ namespace Modern.WindowKit.Platform.Interop { - public class Utf8Buffer : SafeHandle + internal class Utf8Buffer : SafeHandle { private GCHandle _gcHandle; private byte[]? _data; diff --git a/src/Modern.WindowKit/Avalonia.X11/X11CursorFactory.cs b/src/Modern.WindowKit/Avalonia.X11/X11CursorFactory.cs index 24ba4c9..cb5c961 100644 --- a/src/Modern.WindowKit/Avalonia.X11/X11CursorFactory.cs +++ b/src/Modern.WindowKit/Avalonia.X11/X11CursorFactory.cs @@ -118,7 +118,7 @@ private static IntPtr GetNullCursor(IntPtr display) // using (var ctx = renderTarget.CreateDrawingContext()) // { // var r = new Rect(_pixelSize.ToSize(1)); - // ctx.DrawBitmap(RefCountable.CreateUnownedNotClonable(bitmap), 1, r, r); + // ctx.DrawBitmap(bitmap, 1, r, r); // } // Handle = XLib.XcursorImageLoadCursor(display, _blob.Address); diff --git a/src/Modern.WindowKit/Avalonia.X11/X11Platform.cs b/src/Modern.WindowKit/Avalonia.X11/X11Platform.cs index 7c45f64..6828c75 100644 --- a/src/Modern.WindowKit/Avalonia.X11/X11Platform.cs +++ b/src/Modern.WindowKit/Avalonia.X11/X11Platform.cs @@ -75,7 +75,6 @@ public void Initialize(X11PlatformOptions options) // .Bind().ToConstant(this) // .Bind().ToConstant(new X11PlatformThreading(this)) // .Bind().ToConstant(new SleepLoopRenderTimer(60)) - // .Bind().ToConstant(new RenderLoop()) // .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control)) // .Bind().ToFunc(() => KeyboardDevice) // .Bind().ToConstant(new X11CursorFactory(Display)) @@ -104,7 +103,7 @@ public void Initialize(X11PlatformOptions options) //var gl = AvaloniaLocator.Current.GetService(); - //Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService(), gl); + //Compositor = new Compositor(gl); } public IntPtr DeferredDisplay { get; set; } diff --git a/src/Modern.WindowKit/Avalonia.X11/X11Window.cs b/src/Modern.WindowKit/Avalonia.X11/X11Window.cs index f7a8c9b..e3ffc89 100644 --- a/src/Modern.WindowKit/Avalonia.X11/X11Window.cs +++ b/src/Modern.WindowKit/Avalonia.X11/X11Window.cs @@ -196,7 +196,7 @@ public X11Window(AvaloniaX11Platform platform, IWindowImpl? popupParent) _rawEventGrouper = new RawEventGrouper(DispatchInput, platform.EventGrouperDispatchQueue); _transparencyHelper = new TransparencyHelper(_x11, _handle, platform.Globals); - _transparencyHelper.SetTransparencyRequest(WindowTransparencyLevel.None); + _transparencyHelper.SetTransparencyRequest(Array.Empty()); CreateIC(); @@ -382,9 +382,8 @@ public Action? TransparencyLevelChanged public Action? PositionChanged { get; set; } public Action? LostFocus { get; set; } - //public IRenderer CreateRenderer(IRenderRoot root) => - // new CompositingRenderer(root, _platform.Compositor, () => Surfaces); - + //public Compositor Compositor => _platform.Compositor; + private void OnEvent(ref XEvent ev) { if (_inputRoot is null) @@ -511,7 +510,7 @@ private void OnEvent(ref XEvent ev) if (changedSize && !updatedSizeViaScaling && !_popup) Resized?.Invoke(ClientSize, WindowResizeReason.Unspecified); - }, DispatcherPriority.Layout); + }, DispatcherPriority.AsyncRenderTargetResize); if (_useRenderWindow) XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width, ev.ConfigureEvent.height); @@ -796,7 +795,7 @@ private void EnqueuePaint() { _triggeredExpose = false; DoPaint(); - }, DispatcherPriority.Render); + }, DispatcherPriority.UiThreadRender); } } @@ -1307,8 +1306,10 @@ private void ChangeWMAtoms(bool enable, params IntPtr[] atoms) public IPopupPositioner? PopupPositioner { get; } - public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) => - _transparencyHelper?.SetTransparencyRequest(transparencyLevel); + public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) + { + _transparencyHelper?.SetTransparencyRequest(transparencyLevels); + } public void SetWindowManagerAddShadowHint(bool enabled) { @@ -1325,7 +1326,7 @@ public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { } public bool IsEnabled => !_disabled; - public class SurfacePlatformHandle : IPlatformNativeSurfaceHandle + public class SurfacePlatformHandle : INativePlatformHandleSurface { private readonly X11Window _owner; diff --git a/src/Modern.WindowKit/BclStorageFile.cs b/src/Modern.WindowKit/BclStorageFile.cs index 8555ed2..d013998 100644 --- a/src/Modern.WindowKit/BclStorageFile.cs +++ b/src/Modern.WindowKit/BclStorageFile.cs @@ -64,7 +64,8 @@ public Task OpenReadAsync() public Task OpenWriteAsync() { - return Task.FromResult(FileInfo.OpenWrite()); + var stream = new FileStream(FileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.Write); + return Task.FromResult(stream); } public virtual Task SaveBookmarkAsync() diff --git a/src/Modern.WindowKit/DefaultPlatformSettings.cs b/src/Modern.WindowKit/DefaultPlatformSettings.cs index 90e0f48..c8b4ac5 100644 --- a/src/Modern.WindowKit/DefaultPlatformSettings.cs +++ b/src/Modern.WindowKit/DefaultPlatformSettings.cs @@ -1,12 +1,16 @@ using System; using Modern.WindowKit.Input; +using Modern.WindowKit.Input.Platform; //using Modern.WindowKit.Media; +using Modern.WindowKit.Metadata; +//using Modern.WindowKit.VisualTree; namespace Modern.WindowKit.Platform { /// /// A default implementation of for platforms. /// + [PrivateApi] public class DefaultPlatformSettings : IPlatformSettings { public virtual Size GetTapSize(PointerType type) @@ -28,7 +32,10 @@ public virtual Size GetDoubleTapSize(PointerType type) public virtual TimeSpan GetDoubleTapTime(PointerType type) => TimeSpan.FromMilliseconds(500); public virtual TimeSpan HoldWaitDuration => TimeSpan.FromMilliseconds(300); - + + //public PlatformHotkeyConfiguration HotkeyConfiguration => + // AvaloniaLocator.Current.GetRequiredService(); + public virtual PlatformColorValues GetColorValues() { return new PlatformColorValues diff --git a/src/Modern.WindowKit/Dispatcher.Queue.cs b/src/Modern.WindowKit/Dispatcher.Queue.cs index b062f18..c8ca929 100644 --- a/src/Modern.WindowKit/Dispatcher.Queue.cs +++ b/src/Modern.WindowKit/Dispatcher.Queue.cs @@ -84,6 +84,11 @@ public void UpdateTimer(long? dueTimeInMs) } } + internal static void ResetBeforeUnitTests() + { + s_uiThread = null; + } + internal static void ResetForUnitTests() { if (s_uiThread == null) diff --git a/src/Modern.WindowKit/DispatcherPriority.cs b/src/Modern.WindowKit/DispatcherPriority.cs index d78026b..f175d96 100644 --- a/src/Modern.WindowKit/DispatcherPriority.cs +++ b/src/Modern.WindowKit/DispatcherPriority.cs @@ -1,9 +1,10 @@ using System; using System.ComponentModel; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Threading { - /// + /// /// Defines the priorities with which jobs can be invoked on a . /// public readonly struct DispatcherPriority : IEquatable, IComparable @@ -25,17 +26,17 @@ private DispatcherPriority(int value) internal static readonly DispatcherPriority MinimumForegroundPriority = Default; - /// + /// /// The job will be processed with the same priority as input. /// public static readonly DispatcherPriority Input = new(Default - 1); - /// + /// /// The job will be processed after other non-idle operations have completed. /// public static readonly DispatcherPriority Background = new(Input - 1); - /// + /// /// The job will be processed after background operations have completed. /// public static readonly DispatcherPriority ContextIdle = new(Background - 1); @@ -43,17 +44,17 @@ private DispatcherPriority(int value) /// /// The job will be processed when the application is idle. - /// + /// public static readonly DispatcherPriority ApplicationIdle = new (ContextIdle - 1); /// /// The job will be processed when the system is idle. - /// + /// public static readonly DispatcherPriority SystemIdle = new(ApplicationIdle - 1); /// /// Minimum possible priority that's actually dispatched, default value - /// + /// internal static readonly DispatcherPriority MinimumActiveValue = new(SystemIdle); @@ -79,30 +80,40 @@ private DispatcherPriority(int value) public static readonly DispatcherPriority Loaded = new(Default + 1); /// - /// The job will be processed with the same priority as render. + /// A special priority for platforms with UI render timer or for forced full rasterization requests /// - public static readonly DispatcherPriority Render = new(Loaded + 1); + [PrivateApi] + public static readonly DispatcherPriority UiThreadRender = new(Loaded + 1); /// - /// The job will be processed with the same priority as composition updates. + /// A special priority to synchronize native control host positions, IME, etc + /// We should probably have a better API for that, so the priority is internal /// - public static readonly DispatcherPriority Composition = new(Render + 1); - + internal static readonly DispatcherPriority AfterRender = new(UiThreadRender + 1); + /// - /// The job will be processed with before composition updates. + /// The job will be processed with the same priority as render. /// - public static readonly DispatcherPriority PreComposition = new(Composition + 1); - + public static readonly DispatcherPriority Render = new(AfterRender + 1); + /// - /// The job will be processed with the same priority as layout. + /// A special platform hook for jobs to be executed before the normal render cycle /// - public static readonly DispatcherPriority Layout = new(PreComposition + 1); - + [PrivateApi] + public static readonly DispatcherPriority BeforeRender = new(Render + 1); + + /// + /// A special priority for platforms that resize the render target in asynchronous-ish matter, + /// should be changed into event grouping in the platform backend render + /// + [PrivateApi] + public static readonly DispatcherPriority AsyncRenderTargetResize = new(BeforeRender + 1); + /// /// The job will be processed with the same priority as data binding. /// - [Obsolete("WPF compatibility"), EditorBrowsable(EditorBrowsableState.Never)] public static readonly DispatcherPriority DataBind = new(Layout); - + [Obsolete("WPF compatibility"), EditorBrowsable(EditorBrowsableState.Never)] public static readonly DispatcherPriority DataBind = new(Render); + /// /// The job will be processed with normal priority. /// @@ -183,12 +194,16 @@ public override string ToString() return nameof(Default); if (this == Loaded) return nameof(Loaded); + if (this == UiThreadRender) + return nameof(UiThreadRender); + if (this == AfterRender) + return nameof(AfterRender); if (this == Render) return nameof(Render); - if (this == Composition) - return nameof(Composition); - if (this == PreComposition) - return nameof(PreComposition); + if (this == BeforeRender) + return nameof(BeforeRender); + if (this == AsyncRenderTargetResize) + return nameof(AsyncRenderTargetResize); if (this == DataBind) return nameof(DataBind); if (this == Normal) diff --git a/src/Modern.WindowKit/EnumExtensions.cs b/src/Modern.WindowKit/EnumExtensions.cs index c026642..15d816e 100644 --- a/src/Modern.WindowKit/EnumExtensions.cs +++ b/src/Modern.WindowKit/EnumExtensions.cs @@ -6,7 +6,7 @@ namespace Modern.WindowKit /// /// Provides extension methods for enums. /// - public static class EnumExtensions + internal static class EnumExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Modern.WindowKit/IBitmapImpl.cs b/src/Modern.WindowKit/IBitmapImpl.cs index 741cf11..da6bad4 100644 --- a/src/Modern.WindowKit/IBitmapImpl.cs +++ b/src/Modern.WindowKit/IBitmapImpl.cs @@ -7,7 +7,7 @@ namespace Modern.WindowKit.Platform /// /// Defines the platform-specific interface for a . /// - [Unstable] + [PrivateApi] public interface IBitmapImpl : IDisposable { /// diff --git a/src/Modern.WindowKit/IInputDevice.cs b/src/Modern.WindowKit/IInputDevice.cs index d33ccb2..2b38496 100644 --- a/src/Modern.WindowKit/IInputDevice.cs +++ b/src/Modern.WindowKit/IInputDevice.cs @@ -3,7 +3,7 @@ namespace Modern.WindowKit.Input { - [NotClientImplementable] + [NotClientImplementable, PrivateApi] public interface IInputDevice { /// diff --git a/src/Modern.WindowKit/IKeyboardDevice.cs b/src/Modern.WindowKit/IKeyboardDevice.cs index 5e295e8..1f4a738 100644 --- a/src/Modern.WindowKit/IKeyboardDevice.cs +++ b/src/Modern.WindowKit/IKeyboardDevice.cs @@ -43,14 +43,8 @@ public enum RawInputModifiers PenBarrelButton = 2048 } - [NotClientImplementable] - public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged + [PrivateApi] + public interface IKeyboardDevice : IInputDevice { - //IInputElement? FocusedElement { get; } - - //void SetFocusedElement( - // IInputElement? element, - // NavigationMethod method, - // KeyModifiers modifiers); } } diff --git a/src/Modern.WindowKit/IMouseDevice.cs b/src/Modern.WindowKit/IMouseDevice.cs index ab05a54..58022df 100644 --- a/src/Modern.WindowKit/IMouseDevice.cs +++ b/src/Modern.WindowKit/IMouseDevice.cs @@ -5,7 +5,7 @@ namespace Modern.WindowKit.Input /// /// Represents a mouse device. /// - [NotClientImplementable] + [PrivateApi] public interface IMouseDevice : IPointerDevice { } diff --git a/src/Modern.WindowKit/IPlatformNativeSurfaceHandle.cs b/src/Modern.WindowKit/IPlatformNativeSurfaceHandle.cs index a83d613..3fbffd9 100644 --- a/src/Modern.WindowKit/IPlatformNativeSurfaceHandle.cs +++ b/src/Modern.WindowKit/IPlatformNativeSurfaceHandle.cs @@ -4,7 +4,7 @@ namespace Modern.WindowKit.Platform { [Unstable] - public interface IPlatformNativeSurfaceHandle : IPlatformHandle + public interface INativePlatformHandleSurface : IPlatformHandle { PixelSize Size { get; } double Scaling { get; } diff --git a/src/Modern.WindowKit/IPlatformSettings.cs b/src/Modern.WindowKit/IPlatformSettings.cs index 87b80fb..9081ad0 100644 --- a/src/Modern.WindowKit/IPlatformSettings.cs +++ b/src/Modern.WindowKit/IPlatformSettings.cs @@ -1,10 +1,15 @@ using System; using Modern.WindowKit.Input; +using Modern.WindowKit.Input.Platform; using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Platform - { - [Unstable] +{ + /// + /// The interface represents a contract for accessing platform-specific settings and information. + /// Some of these settings might be changed by used globally in the OS in runtime. + /// + [NotClientImplementable] public interface IPlatformSettings { /// @@ -32,6 +37,11 @@ public interface IPlatformSettings /// Holding duration between pointer press and when event is fired. /// TimeSpan HoldWaitDuration { get; } + + ///// + ///// Get a configuration for platform-specific hotkeys in an Avalonia application. + ///// + //PlatformHotkeyConfiguration HotkeyConfiguration { get; } /// /// Gets current system color values including dark mode and accent colors. diff --git a/src/Modern.WindowKit/IPlatformThreadingInterface.cs b/src/Modern.WindowKit/IPlatformThreadingInterface.cs index c19d2a0..59aa8cd 100644 --- a/src/Modern.WindowKit/IPlatformThreadingInterface.cs +++ b/src/Modern.WindowKit/IPlatformThreadingInterface.cs @@ -8,7 +8,7 @@ namespace Modern.WindowKit.Platform /// /// Provides platform-specific services relating to threading. /// - [Unstable] + [PrivateApi] public interface IPlatformThreadingInterface { /// diff --git a/src/Modern.WindowKit/IPointer.cs b/src/Modern.WindowKit/IPointer.cs index d6db6d5..62b5d28 100644 --- a/src/Modern.WindowKit/IPointer.cs +++ b/src/Modern.WindowKit/IPointer.cs @@ -1,4 +1,5 @@ -using Modern.WindowKit.Metadata; +//using Modern.WindowKit.Input.GestureRecognizers; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Input { diff --git a/src/Modern.WindowKit/IPointerDevice.cs b/src/Modern.WindowKit/IPointerDevice.cs index 07af821..1227564 100644 --- a/src/Modern.WindowKit/IPointerDevice.cs +++ b/src/Modern.WindowKit/IPointerDevice.cs @@ -3,7 +3,7 @@ namespace Modern.WindowKit.Input { - [NotClientImplementable] + [PrivateApi] public interface IPointerDevice : IInputDevice { /// diff --git a/src/Modern.WindowKit/ITopLevelImpl.cs b/src/Modern.WindowKit/ITopLevelImpl.cs index 42917bf..de6546c 100644 --- a/src/Modern.WindowKit/ITopLevelImpl.cs +++ b/src/Modern.WindowKit/ITopLevelImpl.cs @@ -6,6 +6,7 @@ //using Modern.WindowKit.Layout; using Modern.WindowKit.Metadata; //using Modern.WindowKit.Rendering; +//using Modern.WindowKit.Rendering.Composition; namespace Modern.WindowKit.Platform { @@ -72,10 +73,9 @@ public partial interface ITopLevelImpl : IOptionalFeatureProvider, IDisposable Action? TransparencyLevelChanged { get; set; } ///// - ///// Creates a new renderer for the toplevel. + ///// Gets the compositor that's compatible with the toplevel ///// - ///// The toplevel. - //IRenderer CreateRenderer(IRenderRoot root); + //Compositor Compositor { get; } /// /// Sets the for the toplevel. @@ -89,14 +89,14 @@ public partial interface ITopLevelImpl : IOptionalFeatureProvider, IDisposable /// The point in client coordinates. Point PointToClient(PixelPoint point); - /// + /// /// Converts a point from client to screen coordinates. /// /// The point in client coordinates. /// The point in screen coordinates. PixelPoint PointToScreen(Point point); - /// + /// /// Sets the cursor associated with the toplevel. /// /// The cursor. Use null for default cursor @@ -114,10 +114,10 @@ public partial interface ITopLevelImpl : IOptionalFeatureProvider, IDisposable IPopupImpl? CreatePopup(); - /// + /// /// Sets the hint of the TopLevel. /// - void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel); + void SetTransparencyLevelHint(IReadOnlyList transparencyLevels); /// /// Gets the current of the TopLevel. diff --git a/src/Modern.WindowKit/KeyboardDevice.cs b/src/Modern.WindowKit/KeyboardDevice.cs index b3816ec..513d7fa 100644 --- a/src/Modern.WindowKit/KeyboardDevice.cs +++ b/src/Modern.WindowKit/KeyboardDevice.cs @@ -3,9 +3,11 @@ using Modern.WindowKit.Input.Raw; //using Modern.WindowKit.Input.TextInput; //using Modern.WindowKit.Interactivity; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Input { + [PrivateApi] public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged { //private IInputElement? _focusedElement; @@ -13,7 +15,7 @@ public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; - //public static IKeyboardDevice? Instance => AvaloniaLocator.Current.GetService(); + //internal static KeyboardDevice? Instance => AvaloniaLocator.Current.GetService() as KeyboardDevice; //public IInputManager? InputManager => AvaloniaLocator.Current.GetService(); @@ -190,7 +192,6 @@ public void ProcessRawEvent(RawInputEventArgs e) // KeyEventArgs ev = new KeyEventArgs // { // RoutedEvent = routedEvent, - // Device = this, // Key = keyInput.Key, // KeyModifiers = keyInput.Modifiers.ToKeyModifiers(), // Source = element, @@ -239,7 +240,6 @@ public void ProcessRawEvent(RawInputEventArgs e) //{ // var ev = new TextInputEventArgs() // { - // Device = this, // Text = text.Text, // Source = element, // RoutedEvent = InputElement.TextInputEvent diff --git a/src/Modern.WindowKit/ManagedPopupPositioner.cs b/src/Modern.WindowKit/ManagedPopupPositioner.cs index 991669c..0200f54 100644 --- a/src/Modern.WindowKit/ManagedPopupPositioner.cs +++ b/src/Modern.WindowKit/ManagedPopupPositioner.cs @@ -2,9 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Transactions; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Controls.Primitives.PopupPositioning { + [PrivateApi] public interface IManagedPopupPositionerPopup { IReadOnlyList Screens { get; } @@ -13,6 +15,7 @@ public interface IManagedPopupPositionerPopup void MoveAndResize(Point devicePoint, Size virtualSize); } + [PrivateApi] public class ManagedPopupPositionerScreenInfo { public Rect Bounds { get; } @@ -29,6 +32,7 @@ public ManagedPopupPositionerScreenInfo(Rect bounds, Rect workingArea) /// An implementation for platforms on which a popup can be /// arbitrarily positioned. /// + [PrivateApi] public class ManagedPopupPositioner : IPopupPositioner { private readonly IManagedPopupPositionerPopup _popup; diff --git a/src/Modern.WindowKit/ManagedPopupPositionerPopupImplHelper.cs b/src/Modern.WindowKit/ManagedPopupPositionerPopupImplHelper.cs index 1c006bd..a34dcdb 100644 --- a/src/Modern.WindowKit/ManagedPopupPositionerPopupImplHelper.cs +++ b/src/Modern.WindowKit/ManagedPopupPositionerPopupImplHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Modern.WindowKit.Metadata; using Modern.WindowKit.Platform; namespace Modern.WindowKit.Controls.Primitives.PopupPositioning @@ -8,6 +9,7 @@ namespace Modern.WindowKit.Controls.Primitives.PopupPositioning /// /// This class is used to simplify integration of IPopupImpl implementations with popup positioner /// + [PrivateApi] public class ManagedPopupPositionerPopupImplHelper : IManagedPopupPositionerPopup { private readonly IWindowBaseImpl _parent; diff --git a/src/Modern.WindowKit/MouseDevice.cs b/src/Modern.WindowKit/MouseDevice.cs index 313e0c8..e7e7bfa 100644 --- a/src/Modern.WindowKit/MouseDevice.cs +++ b/src/Modern.WindowKit/MouseDevice.cs @@ -2,8 +2,12 @@ using System.Collections.Generic; using Modern.WindowKit.Reactive; using Modern.WindowKit.Input.Raw; +//using Modern.WindowKit.Interactivity; +using Modern.WindowKit.Metadata; using Modern.WindowKit.Platform; using Modern.WindowKit.Utilities; +//using Modern.WindowKit.VisualTree; +//using Modern.WindowKit.Input.GestureRecognizers; #pragma warning disable CS0618 namespace Modern.WindowKit.Input @@ -11,6 +15,7 @@ namespace Modern.WindowKit.Input /// /// Represents a mouse device. /// + [PrivateApi] public class MouseDevice : IMouseDevice, IDisposable { //private int _clickCount; @@ -124,9 +129,10 @@ private void LeaveWindow() // if (source != null) // { // _pointer.Capture(source); - // if (source != null) + + // var settings = ((IInputRoot?)(source as Interactive)?.GetVisualRoot())?.PlatformSettings; + // if (settings is not null) // { - // var settings = AvaloniaLocator.Current.GetRequiredService(); // var doubleClickTime = settings.GetDoubleTapTime(PointerType.Mouse).TotalMilliseconds; // var doubleClickSize = settings.GetDoubleTapSize(PointerType.Mouse); @@ -139,11 +145,12 @@ private void LeaveWindow() // _lastClickTime = timestamp; // _lastClickRect = new Rect(p, new Size()) // .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); - // _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); - // var e = new PointerPressedEventArgs(source, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount); - // source.RaiseEvent(e); - // return e.Handled; // } + // + // _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); + // var e = new PointerPressedEventArgs(source, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount); + // source.RaiseEvent(e); + // return e.Handled; // } // return false; @@ -156,17 +163,21 @@ private void LeaveWindow() // device = device ?? throw new ArgumentNullException(nameof(device)); // root = root ?? throw new ArgumentNullException(nameof(root)); - // var source = _pointer.Captured ?? hitTest; + // var source = _pointer.CapturedGestureRecognizer?.Target ?? _pointer.Captured ?? hitTest; // if (source is object) // { // var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)root, // p, timestamp, properties, inputModifiers, intermediatePoints); - // source.RaiseEvent(e); + // if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) + // gestureRecognizer.PointerMovedInternal(e); + // else + // source.RaiseEvent(e); // return e.Handled; // } + // return false; //} @@ -176,15 +187,19 @@ private void LeaveWindow() // device = device ?? throw new ArgumentNullException(nameof(device)); // root = root ?? throw new ArgumentNullException(nameof(root)); - // var source = _pointer.Captured ?? hitTest; + // var source = _pointer.CapturedGestureRecognizer?.Target ?? _pointer.Captured ?? hitTest; // if (source is not null) // { // var e = new PointerReleasedEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers, // _lastMouseDownButton); - // source?.RaiseEvent(e); + // if (_pointer.CapturedGestureRecognizer is GestureRecognizer gestureRecognizer) + // gestureRecognizer.PointerReleasedInternal(e); + // else + // source?.RaiseEvent(e); // _pointer.Capture(null); + // _pointer.CaptureGestureRecognizer(null); // _lastMouseDownButton = default; // return e.Handled; // } diff --git a/src/Modern.WindowKit/NonPumpingLockHelper.cs b/src/Modern.WindowKit/NonPumpingLockHelper.cs index 2473c00..88a25e0 100644 --- a/src/Modern.WindowKit/NonPumpingLockHelper.cs +++ b/src/Modern.WindowKit/NonPumpingLockHelper.cs @@ -3,7 +3,7 @@ namespace Modern.WindowKit.Utilities { - public class NonPumpingLockHelper + internal class NonPumpingLockHelper { public interface IHelperImpl { diff --git a/src/Modern.WindowKit/Partials/Pointer.cs b/src/Modern.WindowKit/Partials/Pointer.cs deleted file mode 100644 index 30411b0..0000000 --- a/src/Modern.WindowKit/Partials/Pointer.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Modern.WindowKit.Input -{ - public partial class Pointer - { - public void Dispose() { } - } -} diff --git a/src/Modern.WindowKit/Pointer.cs b/src/Modern.WindowKit/Pointer.cs index f9aa5d8..cf5e175 100644 --- a/src/Modern.WindowKit/Pointer.cs +++ b/src/Modern.WindowKit/Pointer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +//using Modern.WindowKit.Input.GestureRecognizers; //using Modern.WindowKit.VisualTree; namespace Modern.WindowKit.Input @@ -52,6 +53,9 @@ public Pointer(int id, PointerType type, bool isPrimary) // if (Captured is Visual v3) // v3.DetachedFromVisualTree += OnCaptureDetached; + + // if (Captured != null) + // CaptureGestureRecognizer(null); //} //static IInputElement? GetNextCapture(Visual parent) @@ -69,6 +73,31 @@ public Pointer(int id, PointerType type, bool isPrimary) public PointerType Type { get; } public bool IsPrimary { get; } - //public void Dispose() => Capture(null); + + ///// + ///// Gets the gesture recognizer that is currently capturing by the pointer, if any. + ///// + //internal GestureRecognizer? CapturedGestureRecognizer { get; private set; } + + public void Dispose() + { + //Capture(null); + } + + ///// + ///// Captures pointer input to the specified gesture recognizer. + ///// + ///// The gesture recognizer. + ///// + //internal void CaptureGestureRecognizer(GestureRecognizer? gestureRecognizer) + //{ + // if (CapturedGestureRecognizer != gestureRecognizer) + // CapturedGestureRecognizer?.PointerCaptureLostInternal(this); + + // if (gestureRecognizer != null) + // Capture(null); + + // CapturedGestureRecognizer = gestureRecognizer; + //} } } diff --git a/src/Modern.WindowKit/PrivateApiAttribute.cs b/src/Modern.WindowKit/PrivateApiAttribute.cs index a828dff..d469f39 100644 --- a/src/Modern.WindowKit/PrivateApiAttribute.cs +++ b/src/Modern.WindowKit/PrivateApiAttribute.cs @@ -2,7 +2,8 @@ namespace Modern.WindowKit.Metadata; -[AttributeUsage(AttributeTargets.Interface)] +[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Constructor + | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Struct)] public sealed class PrivateApiAttribute : Attribute { diff --git a/src/Modern.WindowKit/RawInputEventArgs.cs b/src/Modern.WindowKit/RawInputEventArgs.cs index 42780b1..4bae801 100644 --- a/src/Modern.WindowKit/RawInputEventArgs.cs +++ b/src/Modern.WindowKit/RawInputEventArgs.cs @@ -1,4 +1,5 @@ using System; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Input.Raw { @@ -11,6 +12,7 @@ namespace Modern.WindowKit.Input.Raw /// pre-processing they are consumed by the relevant and turned into /// standard Avalonia events. /// + [PrivateApi] public class RawInputEventArgs : EventArgs { /// diff --git a/src/Modern.WindowKit/RawKeyEventArgs.cs b/src/Modern.WindowKit/RawKeyEventArgs.cs index 829d009..a99bc5d 100644 --- a/src/Modern.WindowKit/RawKeyEventArgs.cs +++ b/src/Modern.WindowKit/RawKeyEventArgs.cs @@ -1,4 +1,6 @@ -namespace Modern.WindowKit.Input.Raw +using Modern.WindowKit.Metadata; + +namespace Modern.WindowKit.Input.Raw { public enum RawKeyEventType { @@ -6,6 +8,7 @@ public enum RawKeyEventType KeyUp } + [PrivateApi] public class RawKeyEventArgs : RawInputEventArgs { public RawKeyEventArgs( diff --git a/src/Modern.WindowKit/RawMouseWheelEventArgs.cs b/src/Modern.WindowKit/RawMouseWheelEventArgs.cs index 1f13699..961ca84 100644 --- a/src/Modern.WindowKit/RawMouseWheelEventArgs.cs +++ b/src/Modern.WindowKit/RawMouseWheelEventArgs.cs @@ -1,6 +1,9 @@  +using Modern.WindowKit.Metadata; + namespace Modern.WindowKit.Input.Raw { + [PrivateApi] public class RawMouseWheelEventArgs : RawPointerEventArgs { public RawMouseWheelEventArgs( diff --git a/src/Modern.WindowKit/RawPointerEventArgs.cs b/src/Modern.WindowKit/RawPointerEventArgs.cs index 245c4fa..29f3b35 100644 --- a/src/Modern.WindowKit/RawPointerEventArgs.cs +++ b/src/Modern.WindowKit/RawPointerEventArgs.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Input.Raw { @@ -31,6 +32,7 @@ public enum RawPointerEventType /// /// A raw mouse event. /// + [PrivateApi] public class RawPointerEventArgs : RawInputEventArgs { private RawPointerPoint _point; @@ -124,6 +126,7 @@ public Point Position //internal IInputElement? InputHitTestResult { get; set; } } + [PrivateApi] public record struct RawPointerPoint { /// diff --git a/src/Modern.WindowKit/RawTextInputEventArgs.cs b/src/Modern.WindowKit/RawTextInputEventArgs.cs index 00cbde5..3e536b2 100644 --- a/src/Modern.WindowKit/RawTextInputEventArgs.cs +++ b/src/Modern.WindowKit/RawTextInputEventArgs.cs @@ -1,5 +1,8 @@ -namespace Modern.WindowKit.Input.Raw +using Modern.WindowKit.Metadata; + +namespace Modern.WindowKit.Input.Raw { + [PrivateApi] public partial class RawTextInputEventArgs : RawInputEventArgs { //public RawTextInputEventArgs( diff --git a/src/Modern.WindowKit/RawTouchEventArgs.cs b/src/Modern.WindowKit/RawTouchEventArgs.cs index 6dc2aef..a95ba77 100644 --- a/src/Modern.WindowKit/RawTouchEventArgs.cs +++ b/src/Modern.WindowKit/RawTouchEventArgs.cs @@ -1,7 +1,9 @@ using System; +using Modern.WindowKit.Metadata; namespace Modern.WindowKit.Input.Raw { + [PrivateApi] public class RawTouchEventArgs : RawPointerEventArgs { public RawTouchEventArgs(IInputDevice device, ulong timestamp, IInputRoot root, diff --git a/src/Modern.WindowKit/Rect.cs b/src/Modern.WindowKit/Rect.cs index 6f5ce49..a220646 100644 --- a/src/Modern.WindowKit/Rect.cs +++ b/src/Modern.WindowKit/Rect.cs @@ -607,5 +607,12 @@ public static Rect Parse(string s) ); } } + + /// + /// This method should be used internally to check for the rect emptiness + /// Once we add support for WPF-like empty rects, there will be an actual implementation + /// For now it's internal to keep some loud community members happy about the API being pretty + /// + internal bool IsEmpty() => this == default; } } diff --git a/src/Modern.WindowKit/TouchDevice.cs b/src/Modern.WindowKit/TouchDevice.cs index 66284cb..c96453d 100644 --- a/src/Modern.WindowKit/TouchDevice.cs +++ b/src/Modern.WindowKit/TouchDevice.cs @@ -3,7 +3,11 @@ using System.Linq; using System.Reflection; using Modern.WindowKit.Input.Raw; +//using Modern.WindowKit.Interactivity; +using Modern.WindowKit.Metadata; using Modern.WindowKit.Platform; +//using Modern.WindowKit.VisualTree; + #pragma warning disable CS0618 namespace Modern.WindowKit.Input @@ -14,6 +18,7 @@ namespace Modern.WindowKit.Input /// /// This class is supposed to be used on per-toplevel basis, don't use a shared one /// + [PrivateApi] public class TouchDevice : IPointerDevice, IDisposable { private readonly Dictionary _pointers = new Dictionary(); @@ -47,6 +52,7 @@ public void ProcessRawEvent(RawInputEventArgs ev) //} //var target = pointer.Captured ?? args.Root; + //var gestureTarget = pointer.CapturedGestureRecognizer?.Target; //var updateKind = args.Type.ToUpdateKind(); //var keyModifier = args.InputModifiers.ToKeyModifiers(); @@ -60,20 +66,24 @@ public void ProcessRawEvent(RawInputEventArgs ev) // } // else // { - // var settings = AvaloniaLocator.Current.GetRequiredService(); - // var doubleClickTime = settings.GetDoubleTapTime(PointerType.Touch).TotalMilliseconds; - // var doubleClickSize = settings.GetDoubleTapSize(PointerType.Touch); - - // if (!_lastClickRect.Contains(args.Position) - // || ev.Timestamp - _lastClickTime > doubleClickTime) - // { - // _clickCount = 0; - // } - // ++_clickCount; - // _lastClickTime = ev.Timestamp; - // _lastClickRect = new Rect(args.Position, new Size()) - // .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); - //} + // var settings = ((IInputRoot?)(target as Interactive)?.GetVisualRoot())?.PlatformSettings; + // if (settings is not null) + // { + // var doubleClickTime = settings.GetDoubleTapTime(PointerType.Touch).TotalMilliseconds; + // var doubleClickSize = settings.GetDoubleTapSize(PointerType.Touch); + + // if (!_lastClickRect.Contains(args.Position) + // || ev.Timestamp - _lastClickTime > doubleClickTime) + // { + // _clickCount = 0; + // } + + // ++_clickCount; + // _lastClickTime = ev.Timestamp; + // _lastClickRect = new Rect(args.Position, new Size()) + // .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); + // } + // } // target.RaiseEvent(new PointerPressedEventArgs(target, pointer, // (Visual)args.Root, args.Position, ev.Timestamp, @@ -86,28 +96,50 @@ public void ProcessRawEvent(RawInputEventArgs ev) // _pointers.Remove(args.RawPointerId); // using (pointer) // { - // target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, - // (Visual)args.Root, args.Position, ev.Timestamp, - // new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), - // keyModifier, MouseButton.Left)); + // target = gestureTarget ?? target; + // var e = new PointerReleasedEventArgs(target, pointer, + // (Visual)args.Root, args.Position, ev.Timestamp, + // new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), + // keyModifier, MouseButton.Left); + // if (gestureTarget != null) + // { + // pointer?.CapturedGestureRecognizer?.PointerReleasedInternal(e); + // } + // else + // { + // target.RaiseEvent(e); + // } // } //} - //if (args.Type == RawPointerEventType.TouchCancel) - //{ - //_pointers.Remove(args.RawPointerId); - // using (pointer) - // pointer.Capture(null); - //} +// if (args.Type == RawPointerEventType.TouchCancel) +// { +// _pointers.Remove(args.RawPointerId); +// using (pointer) +// { +// pointer?.Capture(null); +// pointer?.CaptureGestureRecognizer(null); +// } +// } - //if (args.Type == RawPointerEventType.TouchUpdate) - //{ - //target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root, - // args.Position, ev.Timestamp, - // new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), - // keyModifier, args.IntermediatePoints)); - } - //} +// if (args.Type == RawPointerEventType.TouchUpdate) +// { +// target = gestureTarget ?? target; +// var e = new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer!, (Visual)args.Root, +// args.Position, ev.Timestamp, +// new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), +// keyModifier, args.IntermediatePoints); + +// if (gestureTarget != null) +// { +// pointer?.CapturedGestureRecognizer?.PointerMovedInternal(e); +//} +// else +// { +// target.RaiseEvent(e); +// } +// } + } public void Dispose() { diff --git a/src/Modern.WindowKit/WindowTransparencyLevel.cs b/src/Modern.WindowKit/WindowTransparencyLevel.cs index 849e335..326da9e 100644 --- a/src/Modern.WindowKit/WindowTransparencyLevel.cs +++ b/src/Modern.WindowKit/WindowTransparencyLevel.cs @@ -1,35 +1,51 @@ -namespace Modern.WindowKit.Controls +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Modern.WindowKit.Controls; + +public readonly record struct WindowTransparencyLevel { - public enum WindowTransparencyLevel - { - /// - /// The window background is Black where nothing is drawn in the window. - /// - None, + private readonly string _value; - /// - /// The window background is Transparent where nothing is drawn in the window. - /// - Transparent, + private WindowTransparencyLevel(string value) + { + _value = value; + } /// - /// The window background is a blur-behind where nothing is drawn in the window. + /// The window background is Black where nothing is drawn in the window. /// - Blur, + public static WindowTransparencyLevel None { get; } = new(nameof(None)); /// - /// The window background is a blur-behind with a high blur radius. This level may fallback to Blur. + /// The window background is Transparent where nothing is drawn in the window. /// - AcrylicBlur, + public static WindowTransparencyLevel Transparent { get; } = new(nameof(Transparent)); /// - /// Force acrylic on some incompatible versions of Windows 10. + /// The window background is a blur-behind where nothing is drawn in the window. /// - ForceAcrylicBlur, + public static WindowTransparencyLevel Blur { get; } = new(nameof(Blur)); /// - /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 + /// The window background is a blur-behind with a high blur radius. This level may fallback to Blur. /// - Mica + public static WindowTransparencyLevel AcrylicBlur { get; } = new(nameof(AcrylicBlur)); + + /// + /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 + /// + public static WindowTransparencyLevel Mica { get; } = new(nameof(Mica)); + + public override string ToString() + { + return _value; } } + +public class WindowTransparencyLevelCollection : ReadOnlyCollection +{ + public WindowTransparencyLevelCollection(IList list) : base(list) + { + } +}