fix: restore window and keyboard interactions with queue
This commit is contained in:
@@ -79,11 +79,14 @@ extension PlayerModel {
|
||||
}
|
||||
|
||||
private func handleKeyEvent(_ event: NSEvent, in window: NSWindow, chord: KeyChord) -> Bool {
|
||||
guard event.window === window, NSApp.modalWindow == nil, window.attachedSheet == nil,
|
||||
!(window.firstResponder is NSTextView), !(window.firstResponder is NSTextField) else { return false }
|
||||
// Preserve focus traversal, control activation/navigation and VoiceOver's own chords.
|
||||
guard event.window === window, NSApp.modalWindow == nil, window.attachedSheet == nil else { return false }
|
||||
let responder = window.firstResponder
|
||||
if let text = responder as? NSTextView, text.isEditable || text.isSelectable { return false }
|
||||
if let text = responder as? NSTextField, text.isEditable || text.isSelectable { return false }
|
||||
// Lists and inert labels are playback surfaces, not text entry or activation controls.
|
||||
// Keep deliberate button/slider focus, traversal and VoiceOver's own chords native.
|
||||
if chord.key == "tab" && chord.modifiers.subtracting(.shift).isEmpty { return false }
|
||||
if window.firstResponder is NSControl,
|
||||
if responder is NSControl, !(responder is NSTableView), !(responder is NSTextField),
|
||||
chord.modifiers.subtracting(.shift).isEmpty,
|
||||
Self.nativeNavigationKeys.contains(chord.key) {
|
||||
return false
|
||||
|
||||
+49
-33
@@ -1809,14 +1809,6 @@ private struct PlaybackSlider: NSViewRepresentable {
|
||||
func castVolumeSliderFeedbackCheck() -> Bool { PlaybackSlider.Slider.feedbackCheck() }
|
||||
#endif
|
||||
|
||||
@MainActor
|
||||
private struct VideoSurface: NSViewRepresentable {
|
||||
let view: NSView
|
||||
|
||||
func makeNSView(context: Context) -> NSView { view }
|
||||
func updateNSView(_ nsView: NSView, context: Context) {}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private struct CaptionSelector: NSViewRepresentable {
|
||||
let model: PlayerModel
|
||||
@@ -1888,22 +1880,46 @@ private struct CaptionSelector: NSViewRepresentable {
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private struct CompactDragSurface: NSViewRepresentable {
|
||||
struct PlaybackSurface: NSViewRepresentable {
|
||||
let model: PlayerModel
|
||||
|
||||
func makeNSView(context: Context) -> DragView { DragView(model: model) }
|
||||
func updateNSView(_ view: DragView, context: Context) {}
|
||||
func updateNSView(_ view: DragView, context: Context) { view.updateVideo() }
|
||||
|
||||
final class DragView: NSView {
|
||||
private let model: PlayerModel
|
||||
private var hoverTracking: NSTrackingArea?
|
||||
private var mouseDownEvent: NSEvent?
|
||||
|
||||
init(model: PlayerModel) {
|
||||
self.model = model
|
||||
super.init(frame: .zero)
|
||||
identifier = NSUserInterfaceItemIdentifier("PlaybackSurface")
|
||||
if let video = model.videoView {
|
||||
video.frame = bounds
|
||||
video.autoresizingMask = [.width, .height]
|
||||
addSubview(video)
|
||||
}
|
||||
updateVideo()
|
||||
}
|
||||
required init?(coder: NSCoder) { nil }
|
||||
|
||||
func updateVideo() {
|
||||
model.videoView?.isHidden = model.fileName == nil || model.castDevice != nil
|
||||
}
|
||||
|
||||
override var mouseDownCanMoveWindow: Bool { false }
|
||||
|
||||
override func hitTest(_ point: NSPoint) -> NSView? {
|
||||
guard super.hitTest(point) != nil else { return nil }
|
||||
// Leave the outer native resize border alone, not the internal queue boundary.
|
||||
if let content = window?.contentView,
|
||||
!content.bounds.insetBy(dx: 5, dy: 5).contains(content.convert(point, from: superview)) {
|
||||
return nil
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
override func updateTrackingAreas() {
|
||||
super.updateTrackingAreas()
|
||||
if let hoverTracking { removeTrackingArea(hoverTracking) }
|
||||
@@ -1916,18 +1932,33 @@ private struct CompactDragSurface: NSViewRepresentable {
|
||||
|
||||
override func mouseEntered(with event: NSEvent) { model.revealControls() }
|
||||
override func mouseMoved(with event: NSEvent) { model.revealControls() }
|
||||
override func mouseExited(with event: NSEvent) { model.hideControls(hideCursor: false) }
|
||||
override func mouseExited(with event: NSEvent) {
|
||||
if model.isCompact { model.hideControls(hideCursor: false) }
|
||||
}
|
||||
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
mouseDownEvent = nil
|
||||
if event.clickCount == 2 {
|
||||
model.toggleCompact()
|
||||
return
|
||||
if model.isCompact { model.toggleCompact() } else { model.toggleFullScreen() }
|
||||
} else {
|
||||
mouseDownEvent = event
|
||||
}
|
||||
}
|
||||
|
||||
override func mouseDragged(with event: NSEvent) {
|
||||
guard let down = mouseDownEvent, let window else { return }
|
||||
mouseDownEvent = nil
|
||||
model.setControlsDragging(true)
|
||||
window?.performDrag(with: event)
|
||||
model.compactWindow.snapAfterDrag()
|
||||
window.performDrag(with: down)
|
||||
if model.isCompact { model.compactWindow.snapAfterDrag() }
|
||||
model.setControlsDragging(false)
|
||||
}
|
||||
|
||||
override func mouseUp(with event: NSEvent) {
|
||||
guard mouseDownEvent != nil else { return }
|
||||
mouseDownEvent = nil
|
||||
if !model.isCompact { model.hideControls() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2350,18 +2381,8 @@ private struct PlayerContent: View {
|
||||
} else {
|
||||
Color.black
|
||||
}
|
||||
if let view = model.videoView {
|
||||
VideoSurface(view: view)
|
||||
.accessibilityLabel("Video")
|
||||
.opacity(model.fileName == nil || model.castDevice != nil ? 0 : 1)
|
||||
.gesture(
|
||||
TapGesture(count: 2).onEnded { model.toggleFullScreen() }
|
||||
.exclusively(before: TapGesture().onEnded { model.hideControls() })
|
||||
)
|
||||
}
|
||||
if model.isCompact {
|
||||
CompactDragSurface(model: model).accessibilityHidden(true)
|
||||
}
|
||||
PlaybackSurface(model: model)
|
||||
.accessibilityLabel("Video")
|
||||
if !model.subtitleText.isEmpty {
|
||||
Text(model.subtitleText)
|
||||
.font(.system(size: max(16, min(28, geometry.size.width / 32)), weight: .semibold))
|
||||
@@ -2703,12 +2724,7 @@ private struct WindowChrome: NSViewRepresentable {
|
||||
}
|
||||
}
|
||||
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
model.setControlsDragging(true)
|
||||
window?.performDrag(with: event)
|
||||
model.compactWindow.snapAfterDrag()
|
||||
model.setControlsDragging(false)
|
||||
}
|
||||
override func hitTest(_ point: NSPoint) -> NSView? { nil }
|
||||
|
||||
func detach() {
|
||||
if let monitor { NSEvent.removeMonitor(monitor) }
|
||||
|
||||
Reference in New Issue
Block a user