summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <[email protected]>2025-04-02 15:47:59 +0200
committerTor Arne Vestbø <[email protected]>2025-04-29 04:02:52 +0200
commit6f67925799407216b999709d86ac180c1106fa9e (patch)
treedfe4b3e38b12725068b74d9fed41bd0a8aec1bac
parentdb7e2d6fd07778d8aa88bf0d92694acd4ab7e0ee (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.h7
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm41
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)
{