| | |
| | | |
| | | 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 { |
| | |
| | | |
| | | - (void)windowWillEnterFullScreen:(NSNotification *)notification { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(notification.object); |
| | | if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { |
| | | auto nsview = [nswindow contentView]; |
| | | if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) { |
| | | 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)) { |
| | | auto nsview = [nswindow contentView]; |
| | | if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) { |
| | | 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)) { |
| | | auto nsview = [nswindow contentView]; |
| | | if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) { |
| | | 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)) { |
| | | auto nsview = [nswindow contentView]; |
| | | if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) { |
| | | 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)) { |
| | | auto nsview = [nswindow contentView]; |
| | | if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) { |
| | | reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent( |
| | | QWK_NSWindowDelegate::DidResize); |
| | | } |
| | | } |
| | | |
| | | @end |
| | | |
| | | @interface QWK_NSViewObserver : NSObject |
| | | - (instancetype)initWithProxy:(QWK::NSWindowProxy*)proxy; |
| | | @end |
| | | |
| | | // |
| | |
| | | None, |
| | | }; |
| | | |
| | | NSWindowProxy(NSWindow *macWindow) { |
| | | nswindow = macWindow; |
| | | g_proxyIndexes->insert(nswindow, this); |
| | | NSWindowProxy(NSView *macView) { |
| | | nsview = macView; |
| | | |
| | | observer = [[QWK_NSViewObserver alloc] initWithProxy:this]; |
| | | [nsview addObserver:observer |
| | | forKeyPath:@"window" |
| | | options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld |
| | | context:nil]; |
| | | } |
| | | |
| | | ~NSWindowProxy() override { |
| | | g_proxyIndexes->remove(nswindow); |
| | | [nsview removeObserver:observer forKeyPath:@"window"]; |
| | | [observer release]; |
| | | } |
| | | |
| | | // Delegate |
| | |
| | | } |
| | | |
| | | void updateSystemButtonRect() { |
| | | if (!screenRectCallback || !systemButtonVisible) { |
| | | return; |
| | | } |
| | | const auto &buttons = systemButtons(); |
| | | const auto &leftButton = buttons[0]; |
| | | const auto &midButton = buttons[1]; |
| | |
| | | auto width = midButton.frame.size.width; |
| | | auto height = midButton.frame.size.height; |
| | | |
| | | auto viewSize = |
| | | nswindow.contentView ? nswindow.contentView.frame.size : nswindow.frame.size; |
| | | auto viewSize = nsview.frame.size; |
| | | QPoint center = screenRectCallback(QSize(viewSize.width, titlebarHeight)).center(); |
| | | |
| | | // The origin of the NSWindow coordinate system is in the lower left corner, we |
| | |
| | | } |
| | | |
| | | inline std::array<NSButton *, 3> systemButtons() { |
| | | auto nswindow = [nsview window]; |
| | | if (!nswindow) { |
| | | return {nullptr, nullptr, nullptr}; |
| | | } |
| | | NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; |
| | | NSButton *minimizeBtn = [nswindow standardWindowButton:NSWindowMiniaturizeButton]; |
| | | NSButton *zoomBtn = [nswindow standardWindowButton:NSWindowZoomButton]; |
| | |
| | | } |
| | | |
| | | inline int titleBarHeight() const { |
| | | auto nswindow = [nsview window]; |
| | | if (!nswindow) { |
| | | return 0; |
| | | } |
| | | NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; |
| | | return closeBtn.superview.frame.size.height; |
| | | } |
| | |
| | | return false; |
| | | |
| | | NSVisualEffectView *effectView = nil; |
| | | NSView *const view = [nswindow contentView]; |
| | | for (NSView *subview in [[view superview] subviews]) { |
| | | for (NSView *subview in [[nsview superview] subviews]) { |
| | | if ([subview isKindOfClass:visualEffectViewClass]) { |
| | | effectView = reinterpret_cast<NSVisualEffectView *>(subview); |
| | | } |
| | |
| | | |
| | | // System title bar |
| | | void setSystemTitleBarVisible(const bool visible) { |
| | | NSView *nsview = [nswindow contentView]; |
| | | if (!nsview) { |
| | | auto nswindow = [nsview window]; |
| | | if (!nswindow) { |
| | | return; |
| | | } |
| | | |
| | |
| | | |
| | | protected: |
| | | static BOOL canBecomeKeyWindow(id obj, SEL sel) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(obj); |
| | | auto nsview = [nswindow contentView]; |
| | | if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) { |
| | | return YES; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | static BOOL canBecomeMainWindow(id obj, SEL sel) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(obj); |
| | | auto nsview = [nswindow contentView]; |
| | | if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) { |
| | | return YES; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(obj); |
| | | auto nsview = [nswindow contentView]; |
| | | if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) { |
| | | styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | auto nswindow = reinterpret_cast<NSWindow *>(obj); |
| | | auto nsview = [nswindow contentView]; |
| | | if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) { |
| | | transparent = YES; |
| | | } |
| | | |
| | |
| | | private: |
| | | Q_DISABLE_COPY(NSWindowProxy) |
| | | |
| | | NSWindow *nswindow = nil; |
| | | NSView *nsview = nil; |
| | | QWK_NSViewObserver* observer = nil; |
| | | |
| | | bool systemButtonVisible = true; |
| | | ScreenRectCallback screenRectCallback; |
| | |
| | | |
| | | auto it = g_proxyList->find(windowId); |
| | | if (it == g_proxyList->end()) { |
| | | NSWindow *nswindow = mac_getNSWindow(windowId); |
| | | const auto proxy = new NSWindowProxy(nswindow); |
| | | NSView *nsview = reinterpret_cast<NSView *>(windowId); |
| | | const auto proxy = new NSWindowProxy(nsview); |
| | | it = g_proxyList->insert(windowId, proxy); |
| | | } |
| | | return it.value(); |
| | |
| | | } |
| | | |
| | | // Allocate new resources |
| | | ensureWindowProxy(winId)->setSystemTitleBarVisible(false); |
| | | const auto proxy = ensureWindowProxy(winId); |
| | | if (proxy) { |
| | | proxy->setSystemButtonVisible(!windowAttribute(QStringLiteral("no-system-buttons")).toBool()); |
| | | proxy->setScreenRectCallback(m_systemButtonAreaCallback); |
| | | proxy->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 ensureWindowProxy(m_windowId)->setBlurEffect(mode); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | } |
| | | |
| | | @implementation QWK_NSViewObserver { |
| | | QWK::NSWindowProxy* _proxy; // Weak reference |
| | | } |
| | | |
| | | - (instancetype)initWithProxy:(QWK::NSWindowProxy*)proxy { |
| | | if (self = [super init]) { |
| | | _proxy = proxy; |
| | | } |
| | | return self; |
| | | } |
| | | |
| | | // Using QEvent::Show to call setSystemTitleBarVisible/updateSystemButtonRect could also work, |
| | | // but observing the window property change via KVO provides more immediate notification when |
| | | // the NSWindow becomes available, making this approach more natural and reliable. |
| | | - (void)observeValueForKeyPath:(NSString*)keyPath |
| | | ofObject:(id)object |
| | | change:(NSDictionary*)change |
| | | context:(void*)context { |
| | | if ([keyPath isEqualToString:@"window"]) { |
| | | NSWindow* newWindow = change[NSKeyValueChangeNewKey]; |
| | | // NSWindow* oldWindow = change[NSKeyValueChangeOldKey]; |
| | | if (newWindow) { |
| | | _proxy->setSystemTitleBarVisible(false); |
| | | _proxy->updateSystemButtonRect(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @end |