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) {