| | |
| | | namespace QWK { |
| | | |
| | | struct NSWindowProxy : public QWK_NSWindowDelegate { |
| | | enum class BlurMode { |
| | | Dark, |
| | | Light, |
| | | None, |
| | | }; |
| | | |
| | | NSWindowProxy(NSWindow *macWindow) { |
| | | nswindow = macWindow; |
| | | g_proxyIndexes->insert(nswindow, this); |
| | | } |
| | | |
| | | ~NSWindowProxy() override { |
| | | if (blurEffect) { |
| | | setBlurEffect(BlurMode::None); |
| | | } |
| | | g_proxyIndexes->remove(nswindow); |
| | | } |
| | | |
| | | void windowWillEnterFullScreen() override { |
| | | qDebug() << "windowWillEnterFullScreen"; |
| | | } |
| | | |
| | | void windowDidEnterFullScreen() override { |
| | | qDebug() << "windowDidEnterFullScreen"; |
| | | } |
| | | |
| | | void windowWillExitFullScreen() override { |
| | | qDebug() << "windowWillExitFullScreen"; |
| | | if (systemButtonRect.isEmpty() || !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; |
| | | } |
| | | } |
| | | |
| | | void windowDidExitFullScreen() override { |
| | | qDebug() << "windowDidExitFullScreen"; |
| | | if (systemButtonRect.isEmpty() || !systemButtonVisible) |
| | | return; |
| | | |
| | | for (const auto &button : systemButtons()) { |
| | | button.hidden = false; |
| | | } |
| | | updateSystemButtonRect(); |
| | | } |
| | | |
| | | void windowDidResize() override { |
| | | qDebug() << "windowDidResize"; |
| | | if (blurEffect) { |
| | | updateBlurEffectSize(); |
| | | } |
| | | |
| | | if (systemButtonRect.isEmpty() || !systemButtonVisible) { |
| | | return; |
| | | } |
| | | updateSystemButtonRect(); |
| | | } |
| | | |
| | | void setSystemButtonVisible(bool visible) { |
| | | systemButtonVisible = visible; |
| | | for (const auto &button : systemButtons()) { |
| | | button.hidden = !visible; |
| | | } |
| | | |
| | | if (systemButtonRect.isEmpty() || !visible) { |
| | | return; |
| | | } |
| | | updateSystemButtonRect(); |
| | | } |
| | | |
| | | void setSystemButtonRect(const QRect &rect) { |
| | | systemButtonRect = rect; |
| | | |
| | | if (rect.isEmpty() || !systemButtonVisible) { |
| | | return; |
| | | } |
| | | updateSystemButtonRect(); |
| | | } |
| | | |
| | | void updateSystemButtonRect() { |
| | | // 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)/ |
| | | |
| | | const auto &buttons = systemButtons(); |
| | | const auto &leftButton = buttons[0]; |
| | | const auto &midButton = buttons[1]; |
| | | const auto &rightButton = buttons[2]; |
| | | |
| | | auto spacing = midButton.frame.origin.x - leftButton.frame.origin.x; |
| | | auto width = midButton.frame.size.width; |
| | | auto height = midButton.frame.size.height; |
| | | |
| | | QPoint center = systemButtonRect.center(); |
| | | |
| | | // 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}; |
| | | } |
| | | |
| | | void setBlurEffect(BlurMode option) { |
| | | if (option == BlurMode::None) { |
| | | if (!blurEffect) { |
| | | return; |
| | | } |
| | | [blurEffect removeFromSuperview]; |
| | | [blurEffect release]; |
| | | blurEffect = nil; |
| | | } else { |
| | | if (!blurEffect) { |
| | | NSView *const view = [nswindow contentView]; |
| | | NSVisualEffectView *const blurView = |
| | | [[visualEffectViewClass alloc] initWithFrame:view.bounds]; |
| | | blurView.material = NSVisualEffectMaterialUnderWindowBackground; |
| | | blurView.blendingMode = NSVisualEffectBlendingModeBehindWindow; |
| | | blurView.state = NSVisualEffectStateFollowsWindowActiveState; |
| | | |
| | | #if 1 |
| | | const NSView *const parent = [view superview]; |
| | | [parent addSubview:blurView positioned:NSWindowBelow relativeTo:view]; |
| | | #endif |
| | | |
| | | blurEffect = blurView; |
| | | updateBlurEffectSize(); |
| | | } |
| | | |
| | | auto view = static_cast<const NSVisualEffectView *>(blurEffect); |
| | | if (option == BlurMode::Dark) { |
| | | view.appearance = [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantDark"]; |
| | | } else { |
| | | view.appearance = |
| | | [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantLight"]; |
| | | } |
| | | } |
| | | } |
| | | |
| | | void updateBlurEffectSize() { |
| | | const NSView *const view = [nswindow contentView]; |
| | | blurEffect.frame = view.frame; |
| | | } |
| | | |
| | | void setSystemTitleBarVisible(const bool visible) { |
| | |
| | | windowObserver = nil; |
| | | } |
| | | |
| | | private: |
| | | static inline const Class windowClass = [NSWindow class]; |
| | | |
| | | static inline const Class visualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); |
| | | |
| | | protected: |
| | | static BOOL canBecomeKeyWindow(id obj, SEL sel) { |
| | | if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | return YES; |
| | |
| | | #endif |
| | | } |
| | | |
| | | static inline const Class windowClass = [NSWindow class]; |
| | | |
| | | private: |
| | | Q_DISABLE_COPY(NSWindowProxy) |
| | | |
| | | NSWindow *nswindow = nil; |
| | | NSView *blurEffect = nil; |
| | | |
| | | bool systemButtonVisible = true; |
| | | QRect systemButtonRect; |
| | | |
| | | static inline QWK_NSWindowObserver *windowObserver = nil; |
| | | |
| | |
| | | void CocoaWindowContext::virtual_hook(int id, void *data) { |
| | | switch (id) { |
| | | case SystemButtonAreaChangedHook: { |
| | | // TODO: mac system button rect updated |
| | | ensureWindowProxy(windowId)->setSystemButtonRect(m_systemButtonArea); |
| | | return; |
| | | } |
| | | |
| | |
| | | |
| | | bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute, |
| | | const QVariant &oldAttribute) { |
| | | Q_UNUSED(oldAttribute) |
| | | |
| | | if (key == QStringLiteral("no-system-buttons")) { |
| | | if (attribute.toBool()) { |
| | | // TODO: set off |
| | | } else { |
| | | // TODO: set on |
| | | } |
| | | if (attribute.type() != QVariant::Bool) |
| | | return false; |
| | | ensureWindowProxy(windowId)->setSystemButtonVisible(!attribute.toBool()); |
| | | return true; |
| | | } |
| | | |
| | | if (key == QStringLiteral("blur-effect")) { |
| | | // Class not available |
| | | if (!NSWindowProxy::visualEffectViewClass) { |
| | | return false; |
| | | } |
| | | |
| | | auto option = NSWindowProxy::BlurMode::None; |
| | | if (attribute.type() == QVariant::Bool) { |
| | | if (attribute.toBool()) { |
| | | NSString *osxMode = |
| | | [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; |
| | | option = [osxMode isEqualToString:@"Dark"] ? NSWindowProxy::BlurMode::Dark |
| | | : NSWindowProxy::BlurMode::Light; |
| | | } |
| | | } else if (attribute.type() == QVariant::String) { |
| | | auto value = attribute.toString(); |
| | | if (value == QStringLiteral("dark")) { |
| | | option = NSWindowProxy::BlurMode::Dark; |
| | | } else if (value == QStringLiteral("light")) { |
| | | option = NSWindowProxy::BlurMode::Light; |
| | | } else if (value == QStringLiteral("none")) { |
| | | // ... |
| | | } else { |
| | | return false; |
| | | } |
| | | } else { |
| | | return false; |
| | | } |
| | | ensureWindowProxy(windowId)->setBlurEffect(option); |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |