Browse Source

Move GetWindowsZOrder to IWindowingPlatform (#20633)

* Move GetWindowsZOrder to IWindowingPlatform

* Update API suppressions
pull/20449/head
Julien Lebosquain 1 month ago
committed by GitHub
parent
commit
816fb91d4e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 36
      api/Avalonia.nupkg.xml
  2. 2
      src/Android/Avalonia.Android/Stubs.cs
  3. 9
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  4. 10
      src/Avalonia.Controls/Platform/IWindowingPlatform.cs
  5. 31
      src/Avalonia.Controls/Window.cs
  6. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  7. 3
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
  8. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  9. 9
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  10. 8
      src/Avalonia.Native/WindowImpl.cs
  11. 77
      src/Avalonia.X11/X11Platform.cs
  12. 74
      src/Avalonia.X11/X11Window.cs
  13. 3
      src/Browser/Avalonia.Browser/WindowingPlatform.cs
  14. 8
      src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs
  15. 16
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  16. 28
      src/Windows/Avalonia.Win32/Win32Platform.cs
  17. 32
      src/Windows/Avalonia.Win32/WindowImpl.cs
  18. 3
      src/iOS/Avalonia.iOS/Stubs.cs
  19. 3
      tests/Avalonia.UnitTests/MockWindowingPlatform.cs

36
api/Avalonia.nupkg.xml

@ -1225,6 +1225,18 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Window.SortWindowsByZOrder(Avalonia.Controls.Window[])</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IWindowImpl.GetWindowsZOrder(System.Span{Avalonia.Controls.Window},System.Span{System.Int64})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target> <Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target>
@ -2125,6 +2137,18 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Window.SortWindowsByZOrder(Avalonia.Controls.Window[])</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.IWindowImpl.GetWindowsZOrder(System.Span{Avalonia.Controls.Window},System.Span{System.Int64})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0002</DiagnosticId> <DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target> <Target>M:Avalonia.Platform.Screen.#ctor(System.Double,Avalonia.PixelRect,Avalonia.PixelRect,System.Boolean)</Target>
@ -2377,6 +2401,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IWindowingPlatform.GetWindowsZOrder(System.ReadOnlySpan{Avalonia.Platform.IWindowImpl},System.Span{System.Int64})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.OpenGL.Surfaces.IGlPlatformSurfaceRenderTarget.BeginDraw(System.Nullable{Avalonia.PixelSize})</Target> <Target>M:Avalonia.OpenGL.Surfaces.IGlPlatformSurfaceRenderTarget.BeginDraw(System.Nullable{Avalonia.PixelSize})</Target>
@ -2575,6 +2605,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left> <Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right> <Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression> </Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.IWindowingPlatform.GetWindowsZOrder(System.ReadOnlySpan{Avalonia.Platform.IWindowImpl},System.Span{System.Int64})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression> <Suppression>
<DiagnosticId>CP0006</DiagnosticId> <DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target> <Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>

2
src/Android/Avalonia.Android/Stubs.cs

@ -12,6 +12,8 @@ namespace Avalonia.Android
public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
public ITrayIconImpl? CreateTrayIcon() => null; public ITrayIconImpl? CreateTrayIcon() => null;
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder) => throw new NotSupportedException();
} }
internal class PlatformIconLoaderStub : IPlatformIconLoader internal class PlatformIconLoaderStub : IPlatformIconLoader

9
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -153,14 +153,5 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
/// <param name="titleBarHeight">-1 for platform default, otherwise the height in DIPs.</param> /// <param name="titleBarHeight">-1 for platform default, otherwise the height in DIPs.</param>
void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight); void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight);
/// <summary>
/// Fills zOrder with numbers that represent the relative order of the windows in the z-order.
/// The topmost window should have the highest number.
/// Both the windows and zOrder lists are expected to be the same length.
/// </summary>
/// <param name="windows">A span of windows to get their z-order</param>
/// <param name="zOrder">Span to be filled with associated window z-order</param>
void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder);
} }
} }

10
src/Avalonia.Controls/Platform/IWindowingPlatform.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Platform namespace Avalonia.Platform
@ -12,5 +13,14 @@ namespace Avalonia.Platform
IWindowImpl CreateEmbeddableWindow(); IWindowImpl CreateEmbeddableWindow();
ITrayIconImpl? CreateTrayIcon(); ITrayIconImpl? CreateTrayIcon();
/// <summary>
/// Fills a span with numbers that represent the relative order of the windows in the z-order.
/// The topmost window should have the highest number.
/// Both the <paramref name="windows"/> and <paramref name="zOrder"/> lists are expected to be the same length.
/// </summary>
/// <param name="windows">A span of windows to get their z-order.</param>
/// <param name="zOrder">The span to be filled with the associated window z-order.</param>
void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder);
} }
} }

31
src/Avalonia.Controls/Window.cs

@ -903,24 +903,25 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Sorts the windows ascending by their Z order - the topmost window will be the last in the list. /// Sorts the windows ascending by their Z order - the topmost window will be the last in the list.
/// </summary> /// </summary>
/// <param name="windows"></param> /// <param name="windows">The windows to sort.</param>
public static void SortWindowsByZOrder(Window[] windows) public static void SortWindowsByZOrder(Span<Window> windows)
{ {
if (windows.Length == 0) if (windows.Length <= 1)
return; return;
if (windows[0].PlatformImpl is not { } platformImpl) var platform = AvaloniaLocator.Current.GetRequiredService<IWindowingPlatform>();
throw new InvalidOperationException("Window.PlatformImpl is null");
var windowImpls = new IWindowImpl[windows.Length];
#if NET5_0_OR_GREATER for (var i = 0; i < windows.Length; ++i)
Span<long> zOrder = stackalloc long[windows.Length]; {
platformImpl.GetWindowsZOrder(windows, zOrder); windowImpls[i] = windows[i].PlatformImpl ??
zOrder.Sort(windows.AsSpan()); throw new ArgumentException($"Invalid window at index {i}", nameof(windows));
#else }
long[] zOrder = new long[windows.Length];
platformImpl.GetWindowsZOrder(windows, zOrder); const int stackAllocThreshold = 128;
Array.Sort(zOrder, windows); var zOrder = windows.Length > stackAllocThreshold ? new long[windows.Length] : stackalloc long[windows.Length];
#endif platform.GetWindowsZOrder(windowImpls, zOrder);
zOrder.Sort(windows);
} }
private void UpdateEnabled() private void UpdateEnabled()

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -170,7 +170,5 @@ namespace Avalonia.DesignerSupport.Remote
public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
{ {
} }
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder) => throw new NotSupportedException();
} }
} }

3
src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs

@ -44,6 +44,9 @@ namespace Avalonia.DesignerSupport.Remote
return s_lastWindow; return s_lastWindow;
} }
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
=> zOrder.Clear();
public static void Initialize(IAvaloniaRemoteTransportConnection transport) public static void Initialize(IAvaloniaRemoteTransportConnection transport)
{ {
s_transport = transport; s_transport = transport;

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -182,8 +182,6 @@ namespace Avalonia.DesignerSupport.Remote
{ {
} }
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder) => throw new NotSupportedException();
public IPopupPositioner? PopupPositioner { get; } public IPopupPositioner? PopupPositioner { get; }
public Action? GotInputWhenDisabled { get; set; } public Action? GotInputWhenDisabled { get; set; }

9
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Compatibility; using Avalonia.Compatibility;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
using Avalonia.Input; using Avalonia.Input;
@ -210,5 +211,13 @@ namespace Avalonia.Native
{ {
return new EmbeddableTopLevelImpl(_factory); return new EmbeddableTopLevelImpl(_factory);
} }
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
{
for (var i = 0; i < windows.Length; i++)
{
zOrder[i] = (windows[i] as WindowImpl)?.ZOrder?.ToInt64() ?? 0;
}
}
} }
} }

8
src/Avalonia.Native/WindowImpl.cs

@ -271,13 +271,5 @@ namespace Avalonia.Native
return base.TryGetFeature(featureType); return base.TryGetFeature(featureType);
} }
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder)
{
for (int i = 0; i < windows.Length; i++)
{
zOrder[i] = (windows[i].PlatformImpl as WindowImpl)?.ZOrder?.ToInt64() ?? 0;
}
}
} }
} }

77
src/Avalonia.X11/X11Platform.cs

@ -230,6 +230,83 @@ namespace Avalonia.X11
throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
} }
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> outputZOrder)
{
// a mapping of parent windows to their children, sorted by z-order (bottom to top)
var windowsChildren = new Dictionary<IntPtr, List<IntPtr>>();
var indexInWindowsSpan = new Dictionary<IntPtr, int>();
for (var i = 0; i < windows.Length; i++)
if (windows[i] is X11Window { Handle: { } handle })
indexInWindowsSpan[handle.Handle] = i;
foreach (var window in windows)
{
if (window is not X11Window x11Window)
continue;
var node = x11Window.Handle.Handle;
while (node != IntPtr.Zero)
{
if (windowsChildren.ContainsKey(node))
{
break;
}
if (XQueryTree(Info.Display, node, out _, out var parent,
out var childrenPtr, out var childrenCount) == 0)
{
break;
}
if (childrenPtr != IntPtr.Zero)
{
unsafe
{
var children = (IntPtr*)childrenPtr;
windowsChildren[node] = new List<IntPtr>(childrenCount);
for (var i = 0; i < childrenCount; i++)
{
windowsChildren[node].Add(children[i]);
}
XFree(childrenPtr);
}
}
node = parent;
}
}
var stack = new Stack<IntPtr>();
var zOrder = 0;
stack.Push(Info.RootWindow);
while (stack.Count > 0)
{
var currentWindow = stack.Pop();
if (!windowsChildren.TryGetValue(currentWindow, out var children))
{
continue;
}
if (indexInWindowsSpan.TryGetValue(currentWindow, out var index))
{
outputZOrder[index] = zOrder;
}
zOrder++;
// Children are returned bottom to top, so we need to push them in reverse order
// In order to traverse bottom children first
for (int i = children.Count - 1; i >= 0; i--)
{
stack.Push(children[i]);
}
}
}
} }
} }

74
src/Avalonia.X11/X11Window.cs

@ -1557,80 +1557,6 @@ namespace Avalonia.X11
} }
/// <inheritdoc/>
public void GetWindowsZOrder(Span<Window> windows, Span<long> outputZOrder)
{
// a mapping of parent windows to their children, sorted by z-order (bottom to top)
var windowsChildren = new Dictionary<IntPtr, List<IntPtr>>();
var indexInWindowsSpan = new Dictionary<IntPtr, int>();
for (var i = 0; i < windows.Length; i++)
if (windows[i].PlatformImpl is { Handle: { } handle })
indexInWindowsSpan[handle.Handle] = i;
foreach (var window in windows)
{
if (window.PlatformImpl is not X11Window x11Window)
continue;
var node = x11Window.Handle.Handle;
while (node != IntPtr.Zero)
{
if (windowsChildren.ContainsKey(node))
{
break;
}
if (XQueryTree(_x11.Display, node, out _, out var parent,
out var childrenPtr, out var childrenCount) == 0)
{
break;
}
if (childrenPtr != IntPtr.Zero)
{
var children = (IntPtr*)childrenPtr;
windowsChildren[node] = new List<IntPtr>(childrenCount);
for (var i = 0; i < childrenCount; i++)
{
windowsChildren[node].Add(children[i]);
}
XFree(childrenPtr);
}
node = parent;
}
}
var stack = new Stack<IntPtr>();
var zOrder = 0;
stack.Push(_x11.RootWindow);
while (stack.Count > 0)
{
var currentWindow = stack.Pop();
if (!windowsChildren.TryGetValue(currentWindow, out var children))
{
continue;
}
if (indexInWindowsSpan.TryGetValue(currentWindow, out var index))
{
outputZOrder[index] = zOrder;
}
zOrder++;
// Children are returned bottom to top, so we need to push them in reverse order
// In order to traverse bottom children first
for (int i = children.Count - 1; i >= 0; i--)
{
stack.Push(children[i]);
}
}
}
public void TakeFocus() public void TakeFocus()
{ {
// TODO: Not yet implemented: need to check if this is required on X11 or not. // TODO: Not yet implemented: need to check if this is required on X11 or not.

3
src/Browser/Avalonia.Browser/WindowingPlatform.cs

@ -74,6 +74,9 @@ internal class BrowserWindowingPlatform : IWindowingPlatform
return null; return null;
} }
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
=> throw new NotSupportedException();
public static KeyboardDevice Keyboard => s_keyboard ?? public static KeyboardDevice Keyboard => s_keyboard ??
throw new InvalidOperationException("BrowserWindowingPlatform not registered."); throw new InvalidOperationException("BrowserWindowingPlatform not registered.");

8
src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs

@ -63,6 +63,14 @@ namespace Avalonia.Headless
public IWindowImpl CreateEmbeddableWindow() => throw new PlatformNotSupportedException(); public IWindowImpl CreateEmbeddableWindow() => throw new PlatformNotSupportedException();
public ITrayIconImpl? CreateTrayIcon() => null; public ITrayIconImpl? CreateTrayIcon() => null;
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
{
for (var i = 0; i < windows.Length; ++i)
{
zOrder[i] = (windows[i] as HeadlessWindowImpl)?.ZOrder ?? 0;
}
}
} }
internal static void Initialize(AvaloniaHeadlessPlatformOptions opts) internal static void Initialize(AvaloniaHeadlessPlatformOptions opts)

16
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -25,7 +25,6 @@ namespace Avalonia.Headless
private WriteableBitmap? _lastRenderedFrame; private WriteableBitmap? _lastRenderedFrame;
private readonly object _sync = new object(); private readonly object _sync = new object();
private readonly PixelFormat _frameBufferFormat; private readonly PixelFormat _frameBufferFormat;
private int _zOrder;
public bool IsPopup { get; } public bool IsPopup { get; }
public HeadlessWindowImpl(bool isPopup, PixelFormat frameBufferFormat) public HeadlessWindowImpl(bool isPopup, PixelFormat frameBufferFormat)
@ -59,6 +58,8 @@ namespace Avalonia.Headless
public Compositor Compositor => AvaloniaHeadlessPlatform.Compositor!; public Compositor Compositor => AvaloniaHeadlessPlatform.Compositor!;
public int ZOrder { get; private set; }
public void SetInputRoot(IInputRoot inputRoot) public void SetInputRoot(IInputRoot inputRoot)
{ {
InputRoot = inputRoot; InputRoot = inputRoot;
@ -82,7 +83,7 @@ namespace Avalonia.Headless
{ {
if (activate) if (activate)
{ {
_zOrder = _nextGlobalZOrder++; ZOrder = _nextGlobalZOrder++;
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input); Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
} }
} }
@ -96,7 +97,7 @@ namespace Avalonia.Headless
public Action<PixelPoint>? PositionChanged { get; set; } public Action<PixelPoint>? PositionChanged { get; set; }
public void Activate() public void Activate()
{ {
_zOrder = _nextGlobalZOrder++; ZOrder = _nextGlobalZOrder++;
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input); Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
} }
@ -430,15 +431,6 @@ namespace Avalonia.Headless
} }
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder)
{
for (int i = 0; i < windows.Length; ++i)
{
if (windows[i].PlatformImpl is HeadlessWindowImpl headlessWindowImpl)
zOrder[i] = headlessWindowImpl._zOrder;
}
}
public void TakeFocus() public void TakeFocus()
{ {
} }

28
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -323,5 +323,33 @@ namespace Avalonia.Win32
if (dpiAwareness != Win32DpiAwareness.Unaware) if (dpiAwareness != Win32DpiAwareness.Unaware)
SetProcessDPIAware(); SetProcessDPIAware();
} }
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
{
var handlesToIndex = new Dictionary<IntPtr, int>(windows.Length);
var outputArray = new long[windows.Length];
for (int i = 0; i < windows.Length; i++)
{
if (windows[i] is WindowImpl platformImpl)
handlesToIndex.Add(platformImpl.Handle.Handle, i);
}
long nextZOrder = 0;
bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
{
if (handlesToIndex.TryGetValue(hWnd, out var index))
{
// We negate the z-order so that the topmost window has the highest number.
outputArray[index] = -nextZOrder;
nextZOrder++;
}
return nextZOrder < outputArray.Length;
}
EnumChildWindows(IntPtr.Zero, EnumWindowsProc, IntPtr.Zero);
outputArray.CopyTo(zOrder);
}
} }
} }

32
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -1613,38 +1613,6 @@ namespace Avalonia.Win32
ExtendClientArea(); ExtendClientArea();
} }
/// <inheritdoc/>
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder)
{
var handlesToIndex = new Dictionary<IntPtr, int>(windows.Length);
var outputArray = new long[windows.Length];
for (int i = 0; i < windows.Length; i++)
{
if (windows[i].PlatformImpl is WindowImpl platformImpl)
handlesToIndex.Add(platformImpl.Handle.Handle, i);
}
long nextZOrder = 0;
bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
{
if (handlesToIndex.TryGetValue(hWnd, out var index))
{
// We negate the z-order so that the topmost window has the highest number.
outputArray[index] = -nextZOrder;
nextZOrder++;
}
return nextZOrder < outputArray.Length;
}
EnumChildWindows(IntPtr.Zero, EnumWindowsProc, IntPtr.Zero);
for (int i = 0; i < windows.Length; i++)
{
zOrder[i] = outputArray[i];
}
}
/// <inheritdoc/> /// <inheritdoc/>
public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended; public bool IsClientAreaExtendedToDecorations => _isClientAreaExtended;

3
src/iOS/Avalonia.iOS/Stubs.cs

@ -25,6 +25,9 @@ namespace Avalonia.iOS
public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
public ITrayIconImpl? CreateTrayIcon() => null; public ITrayIconImpl? CreateTrayIcon() => null;
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
=> throw new NotSupportedException();
} }
internal class PlatformIconLoaderStub : IPlatformIconLoader internal class PlatformIconLoaderStub : IPlatformIconLoader

3
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@ -162,5 +162,8 @@ namespace Avalonia.UnitTests
{ {
return null; return null;
} }
public void GetWindowsZOrder(ReadOnlySpan<IWindowImpl> windows, Span<long> zOrder)
=> zOrder.Clear();
} }
} }

Loading…
Cancel
Save