From fde83a87bccfa56e856f4237e7756ccee48e417d Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 1 Jul 2026 07:42:16 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=94=A8=20NSTrackingArea=20=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3=20.onHover=20=E8=A7=A3=E5=86=B3=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E6=A0=8F=E9=97=AA=E7=83=81=E5=92=8C=E7=BA=A2=E7=BB=BF=E7=81=AF?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .onHover 在 macOS 上不可靠,鼠标离开后会误触发 true 导致标题栏短暂回来但没有红绿灯按钮。 改用 NSTrackingArea 的 mouseEntered/mouseExited 做可靠的鼠标追踪。 --- Sources/MiniPlayerApp.swift | 52 +++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/Sources/MiniPlayerApp.swift b/Sources/MiniPlayerApp.swift index e5b5007..1eaf3eb 100644 --- a/Sources/MiniPlayerApp.swift +++ b/Sources/MiniPlayerApp.swift @@ -223,6 +223,54 @@ class PlayerLayerHostView: NSView { } } +// MARK: - 可靠的鼠标追踪(替代 SwiftUI .onHover 的不可靠行为) +struct MouseTrackingArea: NSViewRepresentable { + let onHoverChange: @MainActor (Bool) -> Void + + func makeNSView(context: Context) -> MouseTrackingNSView { + let view = MouseTrackingNSView() + view.onHoverChange = onHoverChange + return view + } + + func updateNSView(_ nsView: MouseTrackingNSView, context: Context) { + nsView.onHoverChange = onHoverChange + } +} + +class MouseTrackingNSView: NSView { + var onHoverChange: (@MainActor (Bool) -> Void)? + private var trackingArea: NSTrackingArea? + private var isHovering = false + + override func updateTrackingAreas() { + super.updateTrackingAreas() + if let existing = trackingArea { + removeTrackingArea(existing) + } + let area = NSTrackingArea( + rect: .zero, + options: [.mouseEnteredAndExited, .activeInKeyWindow, .inVisibleRect], + owner: self, + userInfo: nil + ) + addTrackingArea(area) + trackingArea = area + } + + override func mouseEntered(with event: NSEvent) { + guard !isHovering else { return } + isHovering = true + onHoverChange?(true) + } + + override func mouseExited(with event: NSEvent) { + guard isHovering else { return } + isHovering = false + onHoverChange?(false) + } +} + // MARK: - 可复用的播放器内容视图(主窗口和全屏共用) struct PlayerContentView: View { @ObservedObject var bridge: PlayerBridge @@ -327,9 +375,9 @@ struct PlayerContentView: View { } } } - .onHover { hovering in + .background(MouseTrackingArea(onHoverChange: { hovering in setWindowTitleBarVisible(hovering) - } + })) .onAppear { // 初始状态:标题栏完全隐藏(含红绿灯按钮) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {