| | |
| | | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) |
| | | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) |
| | | // SPDX-License-Identifier: Apache-2.0 |
| | | |
| | | #include "cocoawindowcontext_p.h" |
| | | |
| | | #include <objc/runtime.h> |
| | | #include <AppKit/AppKit.h> |
| | | |
| | | #include <Cocoa/Cocoa.h> |
| | | |
| | | #include <QtGui/QGuiApplication> |
| | | |
| | | #include "qwkglobal_p.h" |
| | | #include "systemwindow_p.h" |
| | | |
| | | // https://forgetsou.github.io/2020/11/06/macos%E5%BC%80%E5%8F%91-%E5%85%B3%E9%97%AD-%E6%9C%80%E5%B0%8F%E5%8C%96-%E5%85%A8%E5%B1%8F%E5%B1%85%E4%B8%AD%E5%A4%84%E7%90%86(%E4%BB%BFMac%20QQ)/ |
| | | // https://nyrra33.com/2019/03/26/changing-titlebars-height/ |
| | | |
| | | namespace QWK { |
| | | |
| | | struct NSWindowProxy { |
| | | struct NSWindowProxy; |
| | | |
| | | using ProxyList = QHash<WId, NSWindowProxy *>; |
| | | Q_GLOBAL_STATIC(ProxyList, g_proxyList); |
| | | |
| | | using ProxyList2 = QHash<NSWindow *, NSWindowProxy *>; |
| | | Q_GLOBAL_STATIC(ProxyList2, g_proxyIndexes); |
| | | |
| | | } |
| | | |
| | | struct QWK_NSWindowDelegate { |
| | | public: |
| | | enum NSEventType { |
| | | WillEnterFullScreen, |
| | | DidEnterFullScreen, |
| | | WillExitFullScreen, |
| | | DidExitFullScreen, |
| | | DidResize, |
| | | }; |
| | | |
| | | virtual ~QWK_NSWindowDelegate() = default; |
| | | virtual void windowEvent(NSEventType eventType) = 0; |
| | | }; |
| | | |
| | | // |
| | | // Objective C++ Begin |
| | | // |
| | | |
| | | @interface QWK_NSWindowObserver : NSObject { |
| | | } |
| | | @end |
| | | |
| | | @implementation QWK_NSWindowObserver |
| | | |
| | | - (id)init { |
| | | self = [super init]; |
| | | if (self) { |
| | | [[NSNotificationCenter defaultCenter] addObserver:self |
| | | selector:@selector(windowWillEnterFullScreen:) |
| | | name:NSWindowWillEnterFullScreenNotification |
| | | object:nil]; |
| | | [[NSNotificationCenter defaultCenter] addObserver:self |
| | | selector:@selector(windowDidEnterFullScreen:) |
| | | name:NSWindowDidEnterFullScreenNotification |
| | | object:nil]; |
| | | [[NSNotificationCenter defaultCenter] addObserver:self |
| | | selector:@selector(windowWillExitFullScreen:) |
| | | name:NSWindowWillExitFullScreenNotification |
| | | object:nil]; |
| | | [[NSNotificationCenter defaultCenter] addObserver:self |
| | | selector:@selector(windowDidExitFullScreen:) |
| | | name:NSWindowDidExitFullScreenNotification |
| | | object:nil]; |
| | | [[NSNotificationCenter defaultCenter] addObserver:self |
| | | selector:@selector(windowDidResize:) |
| | | name:NSWindowDidResizeNotification |
| | | object:nil]; |
| | | } |
| | | return self; |
| | | } |
| | | |
| | | - (void)dealloc { |
| | | [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| | | [super dealloc]; |
| | | } |
| | | |
| | | - (void)windowWillEnterFullScreen:(NSNotification *)notification { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(notification.object); |
| | | if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { |
| | | reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent( |
| | | QWK_NSWindowDelegate::WillEnterFullScreen); |
| | | } |
| | | } |
| | | |
| | | - (void)windowDidEnterFullScreen:(NSNotification *)notification { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(notification.object); |
| | | if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { |
| | | reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent( |
| | | QWK_NSWindowDelegate::DidEnterFullScreen); |
| | | } |
| | | } |
| | | |
| | | - (void)windowWillExitFullScreen:(NSNotification *)notification { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(notification.object); |
| | | if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { |
| | | reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent( |
| | | QWK_NSWindowDelegate::WillExitFullScreen); |
| | | } |
| | | } |
| | | |
| | | - (void)windowDidExitFullScreen:(NSNotification *)notification { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(notification.object); |
| | | if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { |
| | | reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent( |
| | | QWK_NSWindowDelegate::DidExitFullScreen); |
| | | } |
| | | } |
| | | |
| | | - (void)windowDidResize:(NSNotification *)notification { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(notification.object); |
| | | if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { |
| | | reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent( |
| | | QWK_NSWindowDelegate::DidResize); |
| | | } |
| | | } |
| | | |
| | | @end |
| | | |
| | | // |
| | | // Objective C++ End |
| | | // |
| | | |
| | | namespace QWK { |
| | | |
| | | struct NSWindowProxy : public QWK_NSWindowDelegate { |
| | | enum class BlurMode { |
| | | Dark, |
| | | Light, |
| | | None, |
| | | }; |
| | | |
| | | NSWindowProxy(NSWindow *macWindow) { |
| | | if (instances.contains(macWindow)) { |
| | | nswindow = macWindow; |
| | | g_proxyIndexes->insert(nswindow, this); |
| | | } |
| | | |
| | | ~NSWindowProxy() override { |
| | | g_proxyIndexes->remove(nswindow); |
| | | } |
| | | |
| | | // Delegate |
| | | void windowEvent(NSEventType eventType) override { |
| | | switch (eventType) { |
| | | case WillExitFullScreen: { |
| | | if (!screenRectCallback || !systemButtonVisible) |
| | | return; |
| | | |
| | | // The system buttons will stuck at their default positions when the |
| | | // exit-fullscreen animation is running, we need to hide them until the |
| | | // animation finishes |
| | | for (const auto &button : systemButtons()) { |
| | | button.hidden = true; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | case DidExitFullScreen: { |
| | | if (!screenRectCallback || !systemButtonVisible) |
| | | return; |
| | | |
| | | for (const auto &button : systemButtons()) { |
| | | button.hidden = false; |
| | | } |
| | | updateSystemButtonRect(); |
| | | break; |
| | | } |
| | | |
| | | case DidResize: { |
| | | if (!screenRectCallback || !systemButtonVisible) { |
| | | return; |
| | | } |
| | | updateSystemButtonRect(); |
| | | break; |
| | | } |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // System buttons visibility |
| | | void setSystemButtonVisible(bool visible) { |
| | | systemButtonVisible = visible; |
| | | for (const auto &button : systemButtons()) { |
| | | button.hidden = !visible; |
| | | } |
| | | |
| | | if (!screenRectCallback || !visible) { |
| | | return; |
| | | } |
| | | nswindow = macWindow; |
| | | instances.insert(macWindow, this); |
| | | if (!windowClass) { |
| | | windowClass = [nswindow class]; |
| | | replaceImplementations(); |
| | | } |
| | | updateSystemButtonRect(); |
| | | } |
| | | |
| | | ~NSWindowProxy() { |
| | | instances.remove(nswindow); |
| | | if (instances.count() <= 0) { |
| | | restoreImplementations(); |
| | | windowClass = nil; |
| | | // System buttons area |
| | | void setScreenRectCallback(const ScreenRectCallback &callback) { |
| | | screenRectCallback = callback; |
| | | |
| | | if (!callback || !systemButtonVisible) { |
| | | return; |
| | | } |
| | | nswindow = nil; |
| | | updateSystemButtonRect(); |
| | | } |
| | | |
| | | void replaceImplementations() { |
| | | void updateSystemButtonRect() { |
| | | const auto &buttons = systemButtons(); |
| | | const auto &leftButton = buttons[0]; |
| | | const auto &midButton = buttons[1]; |
| | | const auto &rightButton = buttons[2]; |
| | | |
| | | auto titlebar = rightButton.superview; |
| | | int titlebarHeight = titlebar.frame.size.height; |
| | | |
| | | auto spacing = midButton.frame.origin.x - leftButton.frame.origin.x; |
| | | auto width = midButton.frame.size.width; |
| | | auto height = midButton.frame.size.height; |
| | | |
| | | auto viewSize = |
| | | nswindow.contentView ? nswindow.contentView.frame.size : nswindow.frame.size; |
| | | QPoint center = screenRectCallback(QSize(viewSize.width, titlebarHeight)).center(); |
| | | |
| | | // The origin of the NSWindow coordinate system is in the lower left corner, we |
| | | // do the necessary transformations |
| | | center.ry() = titlebarHeight - center.y(); |
| | | |
| | | // Mid button |
| | | NSPoint centerOrigin = { |
| | | center.x() - width / 2, |
| | | center.y() - height / 2, |
| | | }; |
| | | [midButton setFrameOrigin:centerOrigin]; |
| | | |
| | | // Left button |
| | | NSPoint leftOrigin = { |
| | | centerOrigin.x - spacing, |
| | | centerOrigin.y, |
| | | }; |
| | | [leftButton setFrameOrigin:leftOrigin]; |
| | | |
| | | // Right button |
| | | NSPoint rightOrigin = { |
| | | centerOrigin.x + spacing, |
| | | centerOrigin.y, |
| | | }; |
| | | [rightButton setFrameOrigin:rightOrigin]; |
| | | } |
| | | |
| | | inline std::array<NSButton *, 3> systemButtons() { |
| | | NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; |
| | | NSButton *minimizeBtn = [nswindow standardWindowButton:NSWindowMiniaturizeButton]; |
| | | NSButton *zoomBtn = [nswindow standardWindowButton:NSWindowZoomButton]; |
| | | return {closeBtn, minimizeBtn, zoomBtn}; |
| | | } |
| | | |
| | | inline int titleBarHeight() const { |
| | | NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; |
| | | return closeBtn.superview.frame.size.height; |
| | | } |
| | | |
| | | // Blur effect |
| | | bool setBlurEffect(BlurMode mode) { |
| | | static Class visualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); |
| | | if (!visualEffectViewClass) |
| | | return false; |
| | | |
| | | NSVisualEffectView *effectView = nil; |
| | | NSView *const view = [nswindow contentView]; |
| | | for (NSView *subview in [[view superview] subviews]) { |
| | | if ([subview isKindOfClass:visualEffectViewClass]) { |
| | | effectView = reinterpret_cast<NSVisualEffectView *>(subview); |
| | | } |
| | | } |
| | | if (effectView == nil) { |
| | | return false; |
| | | } |
| | | |
| | | static const auto originalMaterial = effectView.material; |
| | | static const auto originalBlendingMode = effectView.blendingMode; |
| | | static const auto originalState = effectView.state; |
| | | |
| | | if (mode == BlurMode::None) { |
| | | effectView.material = originalMaterial; |
| | | effectView.blendingMode = originalBlendingMode; |
| | | effectView.state = originalState; |
| | | effectView.appearance = nil; |
| | | } else { |
| | | effectView.material = NSVisualEffectMaterialUnderWindowBackground; |
| | | effectView.blendingMode = NSVisualEffectBlendingModeBehindWindow; |
| | | effectView.state = NSVisualEffectStateFollowsWindowActiveState; |
| | | |
| | | if (mode == BlurMode::Dark) { |
| | | effectView.appearance = |
| | | [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantDark"]; |
| | | } else { |
| | | effectView.appearance = |
| | | [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantLight"]; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // System title bar |
| | | void setSystemTitleBarVisible(const bool visible) { |
| | | NSView *nsview = [nswindow contentView]; |
| | | if (!nsview) { |
| | | return; |
| | | } |
| | | |
| | | nsview.wantsLayer = YES; |
| | | nswindow.styleMask |= NSWindowStyleMaskResizable; |
| | | if (visible) { |
| | | nswindow.styleMask &= ~NSWindowStyleMaskFullSizeContentView; |
| | | } else { |
| | | nswindow.styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | nswindow.titlebarAppearsTransparent = (visible ? NO : YES); |
| | | nswindow.titleVisibility = (visible ? NSWindowTitleVisible : NSWindowTitleHidden); |
| | | nswindow.hasShadow = YES; |
| | | // nswindow.showsToolbarButton = NO; |
| | | nswindow.movableByWindowBackground = NO; |
| | | nswindow.movable = NO; // This line causes the window in the wrong position when |
| | | // become fullscreen. |
| | | [nswindow standardWindowButton:NSWindowCloseButton].hidden = NO; |
| | | [nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = NO; |
| | | [nswindow standardWindowButton:NSWindowZoomButton].hidden = NO; |
| | | } |
| | | |
| | | static void replaceImplementations() { |
| | | Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); |
| | | oldSetStyleMask = reinterpret_cast<setStyleMaskPtr>( |
| | | method_setImplementation(method, reinterpret_cast<IMP>(setStyleMask))); |
| | |
| | | method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); |
| | | oldSendEvent = reinterpret_cast<sendEventPtr>( |
| | | method_setImplementation(method, reinterpret_cast<IMP>(sendEvent))); |
| | | |
| | | // Alloc |
| | | windowObserver = [[QWK_NSWindowObserver alloc] init]; |
| | | } |
| | | |
| | | void restoreImplementations() { |
| | | static void restoreImplementations() { |
| | | Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSetStyleMask)); |
| | | oldSetStyleMask = nil; |
| | |
| | | method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSendEvent)); |
| | | oldSendEvent = nil; |
| | | |
| | | // Delete |
| | | [windowObserver release]; |
| | | windowObserver = nil; |
| | | } |
| | | |
| | | void setSystemTitleBarVisible(const bool visible) { |
| | | NSView *nsview = [nswindow contentView]; |
| | | if (!nsview) { |
| | | return; |
| | | } |
| | | nsview.wantsLayer = YES; |
| | | nswindow.styleMask |= NSWindowStyleMaskResizable; |
| | | if (visible) { |
| | | nswindow.styleMask &= ~NSWindowStyleMaskFullSizeContentView; |
| | | } else { |
| | | nswindow.styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | nswindow.titlebarAppearsTransparent = (visible ? NO : YES); |
| | | nswindow.titleVisibility = (visible ? NSWindowTitleVisible : NSWindowTitleHidden); |
| | | nswindow.hasShadow = YES; |
| | | nswindow.showsToolbarButton = NO; |
| | | nswindow.movableByWindowBackground = NO; |
| | | nswindow.movable = NO; // This line causes the window in the wrong position when |
| | | // become fullscreen. |
| | | // For some unknown reason, we don't need the following hack in Qt versions below or |
| | | // equal to 6.2.4. |
| | | #if (QT_VERSION > QT_VERSION_CHECK(6, 2, 4)) |
| | | [nswindow standardWindowButton:NSWindowCloseButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowZoomButton].hidden = (visible ? NO : YES); |
| | | #endif |
| | | } |
| | | static inline const Class windowClass = [NSWindow class]; |
| | | |
| | | private: |
| | | protected: |
| | | static BOOL canBecomeKeyWindow(id obj, SEL sel) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | return YES; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | static BOOL canBecomeMainWindow(id obj, SEL sel) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | return YES; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | transparent = YES; |
| | | } |
| | | |
| | |
| | | Q_DISABLE_COPY(NSWindowProxy) |
| | | |
| | | NSWindow *nswindow = nil; |
| | | |
| | | bool systemButtonVisible = true; |
| | | ScreenRectCallback screenRectCallback; |
| | | |
| | | static inline QWK_NSWindowObserver *windowObserver = nil; |
| | | |
| | | // NSEvent *lastMouseDownEvent = nil; |
| | | |
| | | static inline QHash<NSWindow *, NSWindowProxy *> instances = {}; |
| | | |
| | | static inline Class windowClass = nil; |
| | | |
| | | using setStyleMaskPtr = void (*)(id, SEL, NSWindowStyleMask); |
| | | static inline setStyleMaskPtr oldSetStyleMask = nil; |
| | |
| | | static inline sendEventPtr oldSendEvent = nil; |
| | | }; |
| | | |
| | | using ProxyList = QHash<WId, NSWindowProxy *>; |
| | | Q_GLOBAL_STATIC(ProxyList, g_proxyList); |
| | | |
| | | static inline NSWindow *mac_getNSWindow(const WId windowId) { |
| | | const auto nsview = reinterpret_cast<NSView *>(windowId); |
| | | return [nsview window]; |
| | | } |
| | | |
| | | static inline NSWindowProxy *ensureWindowProxy(const WId windowId) { |
| | | auto it = g_proxyList()->find(windowId); |
| | | if (it == g_proxyList()->end()) { |
| | | if (g_proxyList->isEmpty()) { |
| | | NSWindowProxy::replaceImplementations(); |
| | | } |
| | | |
| | | auto it = g_proxyList->find(windowId); |
| | | if (it == g_proxyList->end()) { |
| | | NSWindow *nswindow = mac_getNSWindow(windowId); |
| | | const auto proxy = new NSWindowProxy(nswindow); |
| | | it = g_proxyList()->insert(windowId, proxy); |
| | | it = g_proxyList->insert(windowId, proxy); |
| | | } |
| | | return it.value(); |
| | | } |
| | | |
| | | static inline void releaseWindowProxy(const WId windowId) { |
| | | if (const auto proxy = g_proxyList()->take(windowId)) { |
| | | if (auto proxy = g_proxyList->take(windowId)) { |
| | | // TODO: Determine if the window is valid |
| | | |
| | | // The window has been destroyed |
| | | // proxy->setSystemTitleBarVisible(true); |
| | | delete proxy; |
| | | } else { |
| | | return; |
| | | } |
| | | |
| | | if (g_proxyList->isEmpty()) { |
| | | NSWindowProxy::restoreImplementations(); |
| | | } |
| | | } |
| | | |
| | | class CocoaWindowEventFilter : public QObject { |
| | | class CocoaWindowEventFilter : public SharedEventFilter { |
| | | public: |
| | | explicit CocoaWindowEventFilter(AbstractWindowContext *context, QObject *parent = nullptr); |
| | | explicit CocoaWindowEventFilter(AbstractWindowContext *context); |
| | | ~CocoaWindowEventFilter() override; |
| | | |
| | | enum WindowStatus { |
| | |
| | | }; |
| | | |
| | | protected: |
| | | bool eventFilter(QObject *object, QEvent *event) override; |
| | | bool sharedEventFilter(QObject *object, QEvent *event) override; |
| | | |
| | | private: |
| | | AbstractWindowContext *m_context; |
| | |
| | | WindowStatus m_windowStatus; |
| | | }; |
| | | |
| | | CocoaWindowEventFilter::CocoaWindowEventFilter(AbstractWindowContext *context, QObject *parent) |
| | | : QObject(parent), m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) { |
| | | m_context->window()->installEventFilter(this); |
| | | CocoaWindowEventFilter::CocoaWindowEventFilter(AbstractWindowContext *context) |
| | | : m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) { |
| | | m_context->installSharedEventFilter(this); |
| | | } |
| | | |
| | | CocoaWindowEventFilter::~CocoaWindowEventFilter() = default; |
| | | |
| | | bool CocoaWindowEventFilter::eventFilter(QObject *obj, QEvent *event) { |
| | | bool CocoaWindowEventFilter::sharedEventFilter(QObject *obj, QEvent *event) { |
| | | Q_UNUSED(obj) |
| | | |
| | | auto type = event->type(); |
| | | if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { |
| | | return false; |
| | |
| | | } |
| | | |
| | | case QEvent::MouseButtonDblClick: { |
| | | if (me->button() == Qt::LeftButton && inTitleBar && |
| | | !delegate->isHostSizeFixed(host)) { |
| | | if (me->button() == Qt::LeftButton && inTitleBar && !m_context->isHostSizeFixed()) { |
| | | Qt::WindowFlags windowFlags = delegate->getWindowFlags(host); |
| | | Qt::WindowStates windowState = delegate->getWindowState(host); |
| | | if (!(windowState & Qt::WindowFullScreen)) { |
| | | if ((windowFlags & Qt::WindowMaximizeButtonHint) && |
| | | !(windowState & Qt::WindowFullScreen)) { |
| | | if (windowState & Qt::WindowMaximized) { |
| | | delegate->setWindowState(host, windowState & ~Qt::WindowMaximized); |
| | | } else { |
| | |
| | | } |
| | | |
| | | CocoaWindowContext::CocoaWindowContext() : AbstractWindowContext() { |
| | | cocoaWindowEventFilter = std::make_unique<CocoaWindowEventFilter>(this); |
| | | } |
| | | |
| | | CocoaWindowContext::~CocoaWindowContext() { |
| | | releaseWindowProxy(windowId); |
| | | releaseWindowProxy(m_windowId); |
| | | } |
| | | |
| | | QString CocoaWindowContext::key() const { |
| | |
| | | |
| | | void CocoaWindowContext::virtual_hook(int id, void *data) { |
| | | switch (id) { |
| | | case ShowSystemMenuHook: { |
| | | // TODO: mac system menu |
| | | return; |
| | | } |
| | | |
| | | case SystemButtonAreaChangedHook: { |
| | | // TODO: mac system button rect updated |
| | | ensureWindowProxy(m_windowId)->setScreenRectCallback(m_systemButtonAreaCallback); |
| | | return; |
| | | } |
| | | |
| | | case WindowAttributeChangedHook: { |
| | | auto args = static_cast<void **>(data); |
| | | const auto &key = *static_cast<const QString *>(args[0]); |
| | | const auto &newVar = *static_cast<const QVariant *>(args[1]); |
| | | const auto &oldVar = *static_cast<const QVariant *>(args[2]); |
| | | |
| | | if (key == QStringLiteral("no-system-buttons")) { |
| | | if (newVar.toBool()) { |
| | | // TODO: set off |
| | | } else { |
| | | // TODO: set on |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | |
| | | default: |
| | |
| | | AbstractWindowContext::virtual_hook(id, data); |
| | | } |
| | | |
| | | void CocoaWindowContext::winIdChanged(QWindow *oldWindow) { |
| | | releaseWindowProxy(windowId); |
| | | if (!m_windowHandle) { |
| | | QVariant CocoaWindowContext::windowAttribute(const QString &key) const { |
| | | if (key == QStringLiteral("title-bar-height")) { |
| | | if (!m_windowId) |
| | | return {}; |
| | | return ensureWindowProxy(m_windowId)->titleBarHeight(); |
| | | } |
| | | return AbstractWindowContext::windowAttribute(key); |
| | | } |
| | | |
| | | void CocoaWindowContext::winIdChanged(WId winId, WId oldWinId) { |
| | | // If the original window id is valid, remove all resources related |
| | | if (oldWinId) { |
| | | releaseWindowProxy(oldWinId); |
| | | } |
| | | |
| | | if (!winId) { |
| | | return; |
| | | } |
| | | |
| | | windowId = m_windowHandle->winId(); |
| | | ensureWindowProxy(windowId)->setSystemTitleBarVisible(false); |
| | | cocoaWindowEventFilter = std::make_unique<CocoaWindowEventFilter>(this, this); |
| | | // Allocate new resources |
| | | ensureWindowProxy(winId)->setSystemTitleBarVisible(false); |
| | | } |
| | | |
| | | bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute, |
| | | const QVariant &oldAttribute) { |
| | | Q_UNUSED(oldAttribute) |
| | | |
| | | Q_ASSERT(m_windowId); |
| | | |
| | | if (key == QStringLiteral("no-system-buttons")) { |
| | | if (attribute.type() != QVariant::Bool) |
| | | return false; |
| | | ensureWindowProxy(m_windowId)->setSystemButtonVisible(!attribute.toBool()); |
| | | return true; |
| | | } |
| | | |
| | | if (key == QStringLiteral("blur-effect")) { |
| | | auto mode = NSWindowProxy::BlurMode::None; |
| | | if (attribute.type() == QVariant::Bool) { |
| | | if (attribute.toBool()) { |
| | | NSString *osxMode = |
| | | [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; |
| | | mode = [osxMode isEqualToString:@"Dark"] ? NSWindowProxy::BlurMode::Dark |
| | | : NSWindowProxy::BlurMode::Light; |
| | | } |
| | | } else if (attribute.type() == QVariant::String) { |
| | | auto value = attribute.toString(); |
| | | if (value == QStringLiteral("dark")) { |
| | | mode = NSWindowProxy::BlurMode::Dark; |
| | | } else if (value == QStringLiteral("light")) { |
| | | mode = NSWindowProxy::BlurMode::Light; |
| | | } else if (value == QStringLiteral("none")) { |
| | | // ... |
| | | } else { |
| | | return false; |
| | | } |
| | | } else { |
| | | return false; |
| | | } |
| | | return ensureWindowProxy(m_windowId)->setBlurEffect(mode); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | } |