Add native fullscreen for macOS sheet-mode player via in-window overlay

The fullscreen button did nothing in sheet mode: AppKit ignores
toggleFullScreen on a window attached as a sheet, and the old fallback
targeted the key window — the sheet itself. Two other strategies were
ruled out first: fullscreening the sheet's parent is silently refused
while any sheet is attached, and resizing the sheet's backing window to
screen size NaN-asserts inside AppKit's sheet positioning even without
animation.

Working approach: swap the sheet for a plain overlay in the main window
and native-fullscreen the now sheet-free main window.

- isMacPlayerFullScreenOverlay on NavigationCoordinator dismisses the
  sheet (presentation binding excludes it; the setter ignores the
  dismissal write-back) and mounts ExpandedPlayerSheet as a black-backed
  overlay in ContentView
- ExpandedPlayerWindowManager fullscreens the parent after the sheet
  detaches, verifies AppKit accepted it (rolls back if refused), hides
  the window toolbar (NSWindow keeps it as a strip above the overlay
  otherwise) and restores it on exit
- a didExitFullScreen observer restores the sheet on any exit path
  (button, Esc, green button, Window menu); closing the player while
  fullscreen exits the main window's fullscreen too
- scheduleSharedViewAdoptionRetry on entry re-homes the shared render
  view once the dismissing sheet's window loses visibility — its steal
  is declined mid-dismissal and nothing else retried, leaving the
  overlay black until the next video load
This commit is contained in:
Arkadiusz Fal
2026-06-25 21:55:47 +02:00
parent 59cc7a8249
commit 9bcb8e35a6
4 changed files with 174 additions and 5 deletions

View File

@@ -57,6 +57,17 @@ struct ContentView: View {
// Mini player overlay (macOS only)
#if os(macOS)
miniPlayerOverlay(appEnvironment: appEnvironment)
// Fullscreen overlay player: replaces the sheet while the main
// window is native-fullscreen (a window with an attached sheet
// can't fullscreen, so the sheet is swapped for this overlay).
if appEnvironment.navigationCoordinator.isMacPlayerFullScreenOverlay {
ZStack {
Color.black
ExpandedPlayerSheet()
}
.ignoresSafeArea()
}
#endif
}
#if os(macOS)
@@ -67,6 +78,9 @@ struct ContentView: View {
}
.onChange(of: appEnvironment.navigationCoordinator.isPlayerExpanded) { _, isExpanded in
if !isExpanded {
// Closing the player while in overlay fullscreen also exits
// the main window's fullscreen (it was entered for the video).
ExpandedPlayerWindowManager.shared.endOverlayFullScreen(exitParentFullScreen: true)
ExpandedPlayerWindowManager.shared.hide()
}
}
@@ -96,9 +110,17 @@ struct ContentView: View {
.sheet(isPresented: Binding(
get: {
appEnvironment.navigationCoordinator.isPlayerExpanded &&
!appEnvironment.settingsManager.macPlayerSeparateWindow
!appEnvironment.settingsManager.macPlayerSeparateWindow &&
!appEnvironment.navigationCoordinator.isMacPlayerFullScreenOverlay
},
set: { appEnvironment.navigationCoordinator.isPlayerExpanded = $0 }
set: { newValue in
// Ignore the dismissal write-back when the sheet is swapped for
// the fullscreen overlay the player stays expanded there.
if !newValue, appEnvironment.navigationCoordinator.isMacPlayerFullScreenOverlay {
return
}
appEnvironment.navigationCoordinator.isPlayerExpanded = newValue
}
)) {
let size = expandedSheetSize(appEnvironment: appEnvironment)
let lockAspect = sheetLockAspectRatio(appEnvironment: appEnvironment)