Skip to content

Commit

Permalink
feat: Add API for fetching window Z-order
Browse files Browse the repository at this point in the history
  • Loading branch information
BAndysc committed Mar 11, 2024
1 parent 38e49b8 commit bd9a23a
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 0 deletions.
2 changes: 2 additions & 0 deletions native/Avalonia.Native/src/OSX/WindowImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ BEGIN_INTERFACE_MAP()
virtual HRESULT GetExtendTitleBarHeight (double*ret) override;

virtual HRESULT SetExtendTitleBarHeight (double value) override;

virtual HRESULT GetWindowZOrder (long* zOrder) override;

void EnterFullScreenMode ();

Expand Down
12 changes: 12 additions & 0 deletions native/Avalonia.Native/src/OSX/WindowImpl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,18 @@
}
}

HRESULT WindowImpl::GetWindowZOrder(long* zOrder) {
START_COM_CALL;
@autoreleasepool {
if (zOrder == nullptr) {
return E_POINTER;
}

*zOrder = [Window orderedIndex];
return S_OK;
}
}

HRESULT WindowImpl::TakeFocusFromChildren() {
START_COM_CALL;

Expand Down
6 changes: 6 additions & 0 deletions src/Avalonia.Controls/Platform/IWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ public interface IWindowImpl : IWindowBaseImpl
/// </summary>
Thickness OffScreenMargin { get; }

/// <summary>
/// Gets the window's z-order position. The highest value is the front, lower numbers are further back.
/// If the z-order can't be determined or the platform doesn't support window stacking, null is returned.
/// </summary>
IntPtr? ZOrder { get; }

/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Avalonia.Controls/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ public PixelPoint Position
set => PlatformImpl?.Move(value);
}

/// <summary>
/// Gets the window's z-order position. The highest value is the front, lower numbers are further back.
/// If the z-order can't be determined or the platform doesn't support window stacking, null is returned.
/// </summary>
public IntPtr? WindowZOrder => PlatformImpl?.ZOrder;

/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)

public double DesktopScaling => 1.0;
public PixelPoint Position { get; set; }
public IntPtr? ZOrder => null;
public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.DesignerSupport/Remote/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class WindowStub : IWindowImpl, IPopupImpl
public IPopupImpl CreatePopup() => new WindowStub(this);

public PixelPoint Position { get; set; }
public IntPtr? ZOrder => null;
public Action<PixelPoint> PositionChanged { get; set; }
public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }
Expand Down
9 changes: 9 additions & 0 deletions src/Avalonia.Native/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ public WindowState WindowState

public Thickness OffScreenMargin { get; } = new Thickness();

public IntPtr? ZOrder
{
get
{
// macOS returns z-order in reverse order - the topmost window has the lowest z-order
return new IntPtr(long.MaxValue - _native.WindowZOrder.ToInt64());
}
}

private bool _isExtended;
public bool IsClientAreaExtendedToDecorations => _isExtended;

Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Native/avn.idl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@clr-access internal
@clr-map bool int
@clr-map u_int64_t ulong
@clr-map long IntPtr
@cpp-preamble @@
#pragma once
#include "com.h"
Expand Down Expand Up @@ -750,6 +751,7 @@ interface IAvnWindow : IAvnWindowBase
HRESULT SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints);
HRESULT GetExtendTitleBarHeight(double*ret);
HRESULT SetExtendTitleBarHeight(double value);
HRESULT GetWindowZOrder(long*ret);
}

[uuid(939b6599-40a8-4710-a4c8-5d72d8f174fb)]
Expand Down
40 changes: 40 additions & 0 deletions src/Avalonia.X11/X11Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,46 @@ public PixelPoint Position
}
}

public IntPtr? ZOrder
{
get
{
var stack = new Stack<IntPtr>();
var zOrder = 0;
stack.Push(_x11.RootWindow);

while (stack.Count > 0)
{
var currentWindow = stack.Pop();

if (currentWindow == _handle)
{
return new IntPtr(zOrder);
}

zOrder++;

if (XQueryTree(_x11.Display, currentWindow, out _, out _,
out var childrenPtr, out var childrenCount) != 0)
{
if (childrenPtr == IntPtr.Zero)
continue;

var children = (IntPtr*)childrenPtr;

// 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 = childrenCount - 1; i >= 0; i--)
stack.Push(children[i]);

XFree(childrenPtr);
}
}

return null;
}
}

public IMouseDevice MouseDevice => _mouse;
public TouchDevice TouchDevice => _touch;

Expand Down
1 change: 1 addition & 0 deletions src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void BeginResizeDrag(WindowEdge edge)

public PixelPoint Position { get; set; }
public Action<PixelPoint>? PositionChanged { get; set; }
public IntPtr? ZOrder => null;
public void Activate()
{
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
Expand Down
14 changes: 14 additions & 0 deletions src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,20 @@ public static extern IntPtr CreateWindowEx(
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(SystemMetric smIndex);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindow(IntPtr hWnd, GetWindowCommand uCmd);

public enum GetWindowCommand : uint
{
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6,
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4
}

[DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW", ExactSpelling = true)]
public static extern uint GetWindowLongPtr(IntPtr hWnd, int nIndex);

Expand Down
23 changes: 23 additions & 0 deletions src/Windows/Avalonia.Win32/WindowImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,29 @@ public void SetParent(IWindowImpl? parent)

public void SetEnabled(bool enable) => EnableWindow(_hwnd, enable);

public IntPtr? ZOrder
{
get
{
var lowestHwnd = GetWindow(_hwnd, GetWindowCommand.GW_HWNDLAST);

var zOrder = 0;
var hwnd = lowestHwnd;
while (hwnd != IntPtr.Zero)
{
if (_hwnd == hwnd)
{
return new IntPtr(zOrder);
}

hwnd = GetWindow(hwnd, GetWindowCommand.GW_HWNDPREV);
zOrder++;
}

return null;
}
}

public void BeginMoveDrag(PointerPressedEventArgs e)
{
e.Pointer.Capture(null);
Expand Down

0 comments on commit bd9a23a

Please sign in to comment.