diff options
author | Tor Arne Vestbø <[email protected]> | 2025-04-02 15:47:59 +0200 |
---|---|---|
committer | Tor Arne Vestbø <[email protected]> | 2025-04-29 04:02:52 +0200 |
commit | 6f67925799407216b999709d86ac180c1106fa9e (patch) | |
tree | dfe4b3e38b12725068b74d9fed41bd0a8aec1bac | |
parent | db7e2d6fd07778d8aa88bf0d92694acd4ab7e0ee (diff) |
macOS: Add native interface API to manage visual effect view
NSVisualEffectView is the only supported way to add effects to
views, such as showing a blurred representation of the content
under the window, or within the window.
Now that we render the content of our QNSView into a sublayer
of the view's root layer, we can add NSVisualEffectViews as
child views without obscuring the view's own content, by making
sure that the NSVisualEffectView layer has a lower Z order in
the layer tree of our view's root layer. This works because
adding a layer-backed or -hosted view as a subview will also
add its layer as a sublayer of the view's layer, making the
effect layer and our content layer sibling layers.
Change-Id: Iab822e8462f54025559b3e3f26c7df668c885d75
Reviewed-by: Richard Moe Gustavsen <[email protected]>
-rw-r--r-- | src/gui/kernel/qplatformwindow_p.h | 7 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.h | 8 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 41 |
3 files changed, 55 insertions, 1 deletions
diff --git a/src/gui/kernel/qplatformwindow_p.h b/src/gui/kernel/qplatformwindow_p.h index e240da5838c..5ee14c90e8a 100644 --- a/src/gui/kernel/qplatformwindow_p.h +++ b/src/gui/kernel/qplatformwindow_p.h @@ -30,6 +30,10 @@ struct wl_surface; #if defined(Q_OS_MACOS) Q_FORWARD_DECLARE_OBJC_CLASS(CALayer); +typedef long NSInteger; +enum NSVisualEffectMaterial : NSInteger; +enum NSVisualEffectBlendingMode : NSInteger; +enum NSVisualEffectState: NSInteger; #endif QT_BEGIN_NAMESPACE @@ -63,6 +67,9 @@ struct Q_GUI_EXPORT QCocoaWindow virtual void setContentBorderEnabled(bool enable) = 0; virtual QPoint bottomLeftClippedByNSWindowOffset() const = 0; virtual CALayer *contentLayer() const = 0; + virtual void manageVisualEffectArea(quintptr identifier, const QRect &rect, + NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode, + NSVisualEffectState activationState) = 0; }; #endif diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index a3ce192e6e3..c7a5d55344a 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -19,11 +19,12 @@ #include <MoltenVK/mvk_vulkan.h> #endif -#include <QHash> +#include <QtCore/private/qflatmap_p.h> Q_FORWARD_DECLARE_OBJC_CLASS(NSWindow); Q_FORWARD_DECLARE_OBJC_CLASS(NSView); Q_FORWARD_DECLARE_OBJC_CLASS(NSCursor); +Q_FORWARD_DECLARE_OBJC_CLASS(NSVisualEffectView); #if !defined(__OBJC__) using NSInteger = long; @@ -227,6 +228,11 @@ public: // for QNSView CALayer *contentLayer() const override; + void manageVisualEffectArea(quintptr identifier, const QRect &rect, + NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode, + NSVisualEffectState activationState) override; + QFlatMap<quintptr, NSVisualEffectView*> m_effectViews; + NSView *m_view = nil; QCocoaNSWindow *m_nsWindow = nil; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 9a8e5246b80..d0abb8e38c4 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -2224,6 +2224,47 @@ CALayer *QCocoaWindow::contentLayer() const return layer; } +void QCocoaWindow::manageVisualEffectArea(quintptr identifier, const QRect &rect, + NSVisualEffectMaterial material, NSVisualEffectBlendingMode blendMode, + NSVisualEffectState activationState) +{ + if (!qt_objc_cast<QContainerLayer*>(m_view.layer)) { + qCWarning(lcQpaWindow) << "Can not manage visual effect areas" + << "in views without a container layer"; + return; + } + + qCDebug(lcQpaWindow) << "Updating visual effect area" << identifier + << "to" << rect << "with material" << material << "blend mode" + << blendMode << "and activation state" << activationState; + + NSVisualEffectView *effectView = nullptr; + if (m_effectViews.contains(identifier)) { + effectView = m_effectViews.value(identifier); + if (rect.isEmpty()) { + [effectView removeFromSuperview]; + m_effectViews.remove(identifier); + return; + } + } else if (!rect.isEmpty()) { + effectView = [NSVisualEffectView new]; + // Ensure that the visual effect layer is stacked well + // below our content layer (which defaults to a z of 0). + effectView.wantsLayer = YES; + effectView.layer.zPosition = -FLT_MAX; + [m_view addSubview:effectView]; + m_effectViews.insert(identifier, effectView); + } + + if (!effectView) + return; + + effectView.frame = rect.toCGRect(); + effectView.material = material; + effectView.blendingMode = blendMode; + effectView.state = activationState; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QCocoaWindow *window) { |