From db65062a7e675249bbdc1ba09ab39b95565bfe3f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 11 Sep 2026 22:59:07 -0700 Subject: [PATCH] MacOS: Make sure macOS child window layer is not lower than parent window. --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 7 +++- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 7 +++- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 34 ++++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index 8a188c89b3..1f252ebad3 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -25,9 +25,14 @@ private: PopupImpl(IAvnWindowEvents* events) : TopLevelImpl(events), WindowBaseImpl(events) { WindowEvents = events; - [Window setLevel:NSPopUpMenuWindowLevel]; + UpdateWindowLevel(); } protected: + virtual NSWindowLevel GetBaseWindowLevel() override + { + return NSPopUpMenuWindowLevel; + } + virtual NSWindowStyleMask CalculateStyleMask() override { return NSWindowStyleMaskBorderless; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 2fbbf60bba..4d6dd55da3 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -80,8 +80,12 @@ public: virtual bool CanZoom() { return false; } virtual HRESULT SetParent(IAvnWindowBase* parent) override; - + + void UpdateWindowLevel(); + protected: + virtual NSWindowLevel GetBaseWindowLevel(); + virtual NSWindowStyleMask CalculateStyleMask() = 0; virtual void UpdateAppearance() override; virtual void SetClientSize(NSSize size) override; @@ -101,6 +105,7 @@ protected: AutoFitContentView *StandardContainer; AvnPoint lastPositionSet; bool _shown; + bool _isTopmost = false; std::list> _children; public: diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 5a74b6e88b..a1bbee9193 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -18,6 +18,7 @@ #include "WindowImpl.h" #include "AvnTextInputMethod.h" #include "AvnView.h" +#include @class AutoFitContentView; @@ -206,12 +207,41 @@ HRESULT WindowBaseImpl::SetTopMost(bool value) { START_COM_CALL; @autoreleasepool { - [Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel]; + _isTopmost = value; + + UpdateWindowLevel(); return S_OK; } } +NSWindowLevel WindowBaseImpl::GetBaseWindowLevel() { + return _isTopmost ? NSFloatingWindowLevel : NSNormalWindowLevel; +} + +void WindowBaseImpl::UpdateWindowLevel() { + if (Window == nullptr) + return; + + auto level = GetBaseWindowLevel(); + + // An owned window must be able to come to the front of its owner. + // TODO: It shouldn't be necessary if we used `addChildWindow` API. + auto parent = Parent.tryGet(); + + if (parent != nullptr && parent->Window != nullptr) + level = std::max(level, [parent->Window level]); + + [Window setLevel:level]; + + for (auto iterator = _children.begin(); iterator != _children.end(); iterator++) { + auto child = (*iterator).tryGet(); + + if (child != nullptr) + child->UpdateWindowLevel(); + } +} + HRESULT WindowBaseImpl::Close() { START_COM_CALL; @@ -527,6 +557,8 @@ HRESULT WindowBaseImpl::SetParent(IAvnWindowBase *parent) { UpdateAppearance(); } + UpdateWindowLevel(); + return S_OK; } }