fix: use a nonactivating compact panel across fullscreen spaces

This commit is contained in:
2026-10-25 22:29:11 +01:00
parent 599e2f548a
commit d8fb648c90
8 changed files with 393 additions and 125 deletions
+49 -2
View File
@@ -169,6 +169,7 @@ struct SystemPlaybackChecks {
styleMask: [.titled, .resizable], backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
defer { window.close() }
model.attachWindow(window)
func event(_ code: UInt16, _ text: String, _ flags: NSEvent.ModifierFlags = []) -> NSEvent {
NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: flags, timestamp: 0,
windowNumber: window.windowNumber, context: nil, characters: text,
@@ -252,6 +253,33 @@ struct SystemPlaybackChecks {
let modal = NSApp.beginModalSession(for: window)
require(!model.handleKeyEvent(shortcuts[0], in: window), "modal session blocks player shortcuts")
NSApp.endModalSession(modal)
window.orderOut(nil)
model.setVolume(0)
model.open(URL(fileURLWithPath: CommandLine.arguments[1]).appendingPathComponent("cache.mp4"))
until("keyboard consumer has actual video playback") { model.canTogglePlayback && model.state.has_video }
model.setPlayback(playing: false)
until("keyboard consumer playback pauses") { model.state.status == PLAYER_PAUSED }
require(model.perform(.enterCompact), "video playback accepts compact entry")
until("keyboard consumer compact entry settles") { !model.compactWindow.isTransitioning }
guard let panel = model.compactWindow.presentationWindow as? NSPanel else {
fatalError("FAIL: keyboard consumer requires the compact presentation panel")
}
model.setShowsBufferInfo(false)
require(!model.handleKeyEvent(shortcuts[4], in: window) && !model.showsBufferInfo,
"hidden normal window cannot dispatch compact playback shortcuts")
let compactShortcut = NSEvent.keyEvent(with: .keyDown, location: .zero, modifierFlags: .command,
timestamp: 0, windowNumber: panel.windowNumber, context: nil, characters: "b",
charactersIgnoringModifiers: "b", isARepeat: false, keyCode: 11)!
require(model.handleKeyEvent(compactShortcut, in: panel) && model.showsBufferInfo,
"active compact panel dispatches the configured playback shortcut")
require(!window.isVisible && !window.isKeyWindow && !panel.isVisible && !panel.isKeyWindow,
"keyboard consumer checks never display or key either presentation")
model.compactWindow.restore(resumeFullScreen: false, animated: false)
model.setShowsBufferInfo(false)
focus(lists[0])
require(model.handleKeyEvent(shortcuts[4], in: window) && model.showsBufferInfo
&& model.compactWindow.presentationWindow === window,
"restored normal window resumes dispatching playback shortcuts")
}
static func main() {
@@ -296,7 +324,12 @@ struct SystemPlaybackChecks {
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
defer { window.close() }
let surface = PlaybackSurface.DragView(model: model)
surface.frame = window.contentView!.bounds
window.contentView!.addSubview(surface)
model.attachWindow(window)
model.open(directory.appendingPathComponent("cache.mp4"))
until("queue geometry fixture has actual video playback") { model.canTogglePlayback && model.state.has_video }
let videoFrame = window.frame
model.showsQueue = true
until("queue opening settles") { !model.isQueueTransitioning }
@@ -308,16 +341,30 @@ struct SystemPlaybackChecks {
require(abs(window.frame.width - videoFrame.width) < 0.01 &&
abs(window.frame.height - videoFrame.height) < 0.01,
"closing queue restores video-only window size")
guard let renderer = model.videoView else { fatalError("FAIL: native renderer is unavailable") }
let content = window.contentView
let activationPolicy = NSApp.activationPolicy()
model.open(directory.appendingPathComponent("anamorphic.mp4"))
require(model.isBusy && model.perform(.enterCompact) && model.isCompact,
"compact entry is available before native media opening acknowledges")
until("decoded anamorphic metadata replaces provisional compact geometry") {
model.state.status == PLAYER_PLAYING && model.isCompact && !model.compactWindow.isTransitioning &&
abs(window.frame.width / window.frame.height - 8.0 / 3.0) < 0.01
guard let presentation = model.compactWindow.presentationWindow else { return false }
return model.state.status == PLAYER_PLAYING && model.isCompact && !model.compactWindow.isTransitioning &&
abs(presentation.frame.width / presentation.frame.height - 8.0 / 3.0) < 0.01
}
let panel = model.compactWindow.presentationWindow!
require(panel !== window && panel is NSPanel && panel.styleMask.contains(.nonactivatingPanel)
&& panel.contentView === content && model.videoView === renderer && renderer.window === panel
&& !window.isVisible && !window.isKeyWindow && !panel.isVisible && !panel.isKeyWindow
&& NSApp.activationPolicy() == activationPolicy,
"loading metadata resizes the compact panel without replacing renderer or app policy")
model.closeFile()
require(!model.isCompact && !model.compactWindow.isTransitioning,
"closing loading/loaded media restores immediately")
require(model.compactWindow.presentationWindow === window && window.contentView === content
&& model.videoView === renderer && renderer.window === window && !window.isVisible && !window.isKeyWindow
&& panel.contentView !== content && !panel.isVisible && !panel.isKeyWindow,
"urgent media close returns retained content to the hidden normal window and retires the panel")
until("anamorphic source closes before the ordinary playback checks") { model.state.status == PLAYER_EMPTY }
require(!model.canPerform(.enterCompact), "empty playback does not enable compact entry")