-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add API for fetching window Z-order #14909
Conversation
Edit: Tested Windows (11), macOS 14.3.1, Linux (Ubuntu with XWayland and X) and everything seems to work. |
You can test this PR using the following package version. |
src/Avalonia.Native/WindowImpl.cs
Outdated
// macOS returns z-order in reverse order - the topmost window has the lowest z-order | ||
return new IntPtr(-_native.WindowZOrder.ToInt64()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this negation should happen on native side, since technically Avalonia.Native project isn't supposed to be macOS specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, changed
return E_POINTER; | ||
} | ||
|
||
*zOrder = [Window orderedIndex]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new prop can be used in
_orderTextBox!.Text = MacOSIntegration.GetOrderedIndex(this).ToString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the GetOrderedIndex
to the new WindowZOrder, I guess those tests could be expanded to all desktop platforms now (though rather than checking the absolute z-order value, it should check its relation to the parent window)
You can test this PR using the following package version. |
src/Avalonia.X11/X11Window.cs
Outdated
@@ -1133,6 +1133,46 @@ public PixelPoint Position | |||
} | |||
} | |||
|
|||
public IntPtr? ZOrder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enumerating all windows in the the entire desktop doesn't seem like a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Especially since that code goes deep into the window tree, so stuff like GTK buttons gets enumerated too.
Also, are you sure that stacking order even matches one from XQueryTree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://tronche.com/gui/x/xlib/window-information/XQueryTree.html
That's what the specification says:
The children are listed in current stacking order, from bottommost (first) to topmost (last).
And that seem to be the case:
Screen.Recording.2024-03-13.at.13.29.19.mov
Those nemo
, gedit
and Konsole
on your screeenshot are something different than the windows themselves. Take a look that under Qt Selection Owner for konsole
the order is correct and the windows have the correct title indicating those represent the actual windows. Maybe this is QT thing to comply with the specs?
But I do agree maybe traversing the from root is not perfect. In that case, I guess in order to get correct z-order within the app, I have to traverse up the tree from each window, find a common parent, then find the index within the parent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kekekeks Updated the implementation to traverse only those windows which are required to compute the relative (within the app) z-order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And that seem to be the case
Might be WM-specific behavior. I'm running KWin 5.20.5 with XOrg 1.20.11, no wayland.
You can test this PR using the following package version. |
System vs app windowsOn the public API side, I'd definitely use More generally, I'm a bit concerned about the API usage here. Imagine an user having a two windows app and wondering why the first window has PerformanceI'm also a bit concerned about potential performance problems. The property iterates through most windows on Win32 and X11: that seems a bit heavy weight, shouldn't that be a method instead to indicate that accessing the z-order isn't "free"? A common usage of this new property is probably StabilityContinuing with
Makes me think it isn't. It doesn't seem stable even for a single loop. Shouldn't we provide something like |
See #6479 as well, another issue that can be closed after this PR.
|
The API should probably accept multiple windows at once and maybe just sort those over ZOrder, since what platform backends are returning isn't stable and not really useful as a raw value. |
…that are not required to compute z-order
This reverts commit 691541a.
@MrJul @kekekeks @maxkatz6 I have redesigned the PR to expose a single method:
which sorts the array by windows z order, bottom to top. I have also changed Windows API to use Also Linux X11 version now only calls Unfortunately Windows and Linux need to allocate some additional memory, I don't have an idea how to reduce the allocations. |
Note that lifetime is an entirely optional concept and no non-lifetime related APIs should be put there |
Where could I put it then to access os specific things? I could just put it in Window class, but it might be a bit weird that Window class has a method that in fact has nothing to do with the instance you call it on. But maybe it is a minor inconvenience? |
A static method on the Window would be fine, I think. A few other points:
|
Avalonia/src/Windows/Avalonia.Win32/Win32Platform.cs Lines 213 to 215 in 0b72cdb
Avalonia/src/Avalonia.Native/AvaloniaNativePlatform.cs Lines 182 to 185 in 0b72cdb
Avalonia/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs Lines 59 to 63 in 0b72cdb
Avalonia/src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs Lines 23 to 44 in 0b72cdb
Is that ok? Alternatively, I could move all the logic from |
Just move the comparer method to IWindowBaseImpl. |
@@ -413,5 +413,7 @@ public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) | |||
{ | |||
|
|||
} | |||
|
|||
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder) => throw new NotSupportedException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Headless platform should try to mimic real platform as much as possible, and not throw exceptions.
For example, we can keep static counter and increment it in HeadlessWindow.Activate(), saving locally per window. And use incremented index in GetWindowsZOrder. A simple solution, which can be then used in tests by somebody.
@@ -153,5 +153,7 @@ public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) | |||
public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) | |||
{ | |||
} | |||
|
|||
public void GetWindowsZOrder(Span<Window> windows, Span<long> zOrder) => throw new NotSupportedException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering, if we should use headless windowing subsystem in our previewer code. Unrelated to this PR.
So, do we keep this API in optional IClassicDesktopStyleApplicationLifetime? If so, API diffs should be updated, run |
Sorry, I forgot to move it to the Window class as a static method. Updated |
You can test this PR using the following package version. |
Needs to be double checked by @grokys as well. |
* feat: Add API for fetching window Z-order * Addressed PR comments * Improve X11Window::ZOrder implementation to avoid traversing windows that are not required to compute z-order * Move zOrder API from Window to IWindowingPlatform * Revert "Addressed PR comments" This reverts commit 691541a. * Rename * Missing methods * Move GetWindowsZOrder from IWindowingPlatform to IWindowImpl * Cleanup * Move SortWindowsByZOrder to Window class as a static method * Implement zOrder for HeadlessWindowImpl --------- Co-authored-by: Max Katz <[email protected]>
What does the pull request do?
Adds new API to Window which returns window z-order. The top most window has the highest value.
✅ I have tested Windows, macOS and Linux (Ubuntu both on XWayland and XOrg)! ✅
What is the current behavior?
n/a
What is the updated/expected behavior with this PR?
n/a
How was the solution implemented (if it's not obvious)?
As described in #6309 (comment), it uses
GetWindow
on Windows,NSWindow.orderedIndex
on macOS andXQueryTree
on X. Note: on Windows and X by default the topmost window has the highest zorder value, on macOS it is reversed, thus on macOS the value has to be negated to mimic the same behaviour.My questions:
NSInteger = Int
, which is 4 bytes on 32 bits systems and 8 bytes on 64 bit systems. Therefore I've decided to go with returning IntPtr on c# side. Let me know if instead I should just uselong
.Checklist
Breaking changes
n/a
Obsoletions / Deprecations
n/a
Fixed issues
Fixed #6309
(side note: this is required for proper Dock support wieslawsoltes/Dock#329)