mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 13:12:53 +00:00
Hide the corner layout panels when the macOS player enters native fullscreen
The fullscreen button in the corner layout zoomed the whole video-plus- panels arrangement onto the screen and left the panels there. Now the button hides the panels first (the video grows over them inside the current window), waits for that animation, then runs the native zoom, so the fullscreen space shows the video alone. On exit the window returns to its size first and the panels slide back in, unless the user re-showed them while fullscreen. System-initiated transitions (green button, Window menu, Mission Control) hide the panels as the zoom starts. Hiding the panels re-arms the separate window's aspect lock; an entering flag parks it during the transition and the restore on exit skips the parked replay, so the window keeps its corner size instead of auto- fitting to the video. The preset's remembered panel visibility is left untouched by the fullscreen session.
This commit is contained in:
@@ -56,8 +56,19 @@ final class ExpandedPlayerWindowManager: NSObject {
|
||||
/// is parked here and replayed in `windowDidExitFullScreen`.
|
||||
private var pendingFullScreenAspectRatio: Double?
|
||||
|
||||
/// True between `windowWillEnterFullScreen` and `windowDidEnterFullScreen`
|
||||
/// on the separate player window. The style mask may not carry
|
||||
/// `.fullScreen` yet at will-enter time, and the corner panels hide right
|
||||
/// then (which re-locks the aspect ratio) — the lock must park instead of
|
||||
/// touching a window mid-transition.
|
||||
private var isPlayerWindowEnteringFullScreen = false
|
||||
|
||||
/// Corner layout panels hidden by entering native fullscreen so the video
|
||||
/// alone fills the screen; restored when fullscreen ends.
|
||||
private var cornerPanelsHiddenForFullScreen = false
|
||||
|
||||
private var isPlayerWindowFullScreen: Bool {
|
||||
playerWindow?.styleMask.contains(.fullScreen) ?? false
|
||||
isPlayerWindowEnteringFullScreen || (playerWindow?.styleMask.contains(.fullScreen) ?? false)
|
||||
}
|
||||
|
||||
// Configuration
|
||||
@@ -382,6 +393,31 @@ final class ExpandedPlayerWindowManager: NSObject {
|
||||
|
||||
/// Toggles native fullscreen on the player window.
|
||||
func toggleFullScreen() {
|
||||
guard let target = playerWindow ?? overlayParent else { return }
|
||||
|
||||
// Entering fullscreen from the corner layout: hide the panels first
|
||||
// (the video grows over them inside the current window), and only
|
||||
// then run the fullscreen zoom — the reverse order zooms the whole
|
||||
// video-plus-panels arrangement and then reflows it on screen. The
|
||||
// entering flag is raised now so the aspect re-lock the panel hide
|
||||
// triggers parks instead of auto-fitting the window mid-way.
|
||||
if !target.styleMask.contains(.fullScreen) {
|
||||
if playerWindow != nil { isPlayerWindowEnteringFullScreen = true }
|
||||
if hideCornerPanelsForFullScreen() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Self.cornerPanelHideDuration) { [weak self] in
|
||||
self?.performNativeFullScreenToggle()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
performNativeFullScreenToggle()
|
||||
}
|
||||
|
||||
/// Time the corner layout takes to animate its panels away (its
|
||||
/// `.easeInOut(duration: 0.25)` plus a beat for the last frame).
|
||||
private static var cornerPanelHideDuration: TimeInterval { 0.3 }
|
||||
|
||||
private func performNativeFullScreenToggle() {
|
||||
guard let window = playerWindow else {
|
||||
// Inline overlay presentation: the player already fills the main
|
||||
// window, so fullscreen is just the window's native toggle. The
|
||||
@@ -444,6 +480,10 @@ final class ExpandedPlayerWindowManager: NSObject {
|
||||
|
||||
// Leave the window fullscreen if the user was fullscreen before the
|
||||
// overlay began; only exit a fullscreen entered for the video.
|
||||
// The will-exit observer is already gone, so undo the fullscreen
|
||||
// panel hide here (the next player open re-derives the visibility
|
||||
// from the preset anyway).
|
||||
restoreCornerPanelsAfterFullScreen()
|
||||
if parent.styleMask.contains(.fullScreen), !overlayWasFullScreenAtBegin {
|
||||
parent.toggleFullScreen(nil)
|
||||
}
|
||||
@@ -467,6 +507,15 @@ final class ExpandedPlayerWindowManager: NSObject {
|
||||
}
|
||||
let center = NotificationCenter.default
|
||||
overlayFullScreenObservers = [
|
||||
center.addObserver(
|
||||
forName: NSWindow.willEnterFullScreenNotification,
|
||||
object: parent,
|
||||
queue: .main
|
||||
) { _ in
|
||||
Task { @MainActor in
|
||||
ExpandedPlayerWindowManager.shared.hideCornerPanelsForFullScreen()
|
||||
}
|
||||
},
|
||||
center.addObserver(
|
||||
forName: NSWindow.didEnterFullScreenNotification,
|
||||
object: parent,
|
||||
@@ -483,11 +532,45 @@ final class ExpandedPlayerWindowManager: NSObject {
|
||||
) { _ in
|
||||
Task { @MainActor in
|
||||
AppEnvironment.shared.navigationCoordinator.isMacInlinePlayerFullScreen = false
|
||||
// Mirror of the enter order: the window is back at its
|
||||
// size first, then the panels slide in.
|
||||
ExpandedPlayerWindowManager.shared.restoreCornerPanelsAfterFullScreen()
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// MARK: - Corner Panels In Fullscreen
|
||||
|
||||
/// Native fullscreen means "video only": the corner layout's panels hide
|
||||
/// for the fullscreen session (the layout animates them away and the
|
||||
/// video grows over them), and come back on exit if this hid them. The
|
||||
/// preset's remembered ("last visible") state is left untouched, so a
|
||||
/// fullscreen session doesn't change what the next player open shows.
|
||||
/// Returns whether this call hid the panels.
|
||||
@discardableResult
|
||||
func hideCornerPanelsForFullScreen() -> Bool {
|
||||
let appEnvironment = AppEnvironment.shared
|
||||
guard appEnvironment.navigationCoordinator.isCornerLayoutActive,
|
||||
appEnvironment.settingsManager.landscapeDetailsPanelVisible else { return false }
|
||||
cornerPanelsHiddenForFullScreen = true
|
||||
appEnvironment.settingsManager.landscapeDetailsPanelVisible = false
|
||||
LoggingService.shared.debug("ExpandedPlayerWindowManager: hid corner panels for fullscreen", category: .player)
|
||||
return true
|
||||
}
|
||||
|
||||
/// Restores the panels hidden by `hideCornerPanelsForFullScreen`, unless
|
||||
/// the user showed them again while fullscreen (title tap) — then there is
|
||||
/// nothing to undo.
|
||||
func restoreCornerPanelsAfterFullScreen() {
|
||||
guard cornerPanelsHiddenForFullScreen else { return }
|
||||
cornerPanelsHiddenForFullScreen = false
|
||||
let appEnvironment = AppEnvironment.shared
|
||||
guard !appEnvironment.settingsManager.landscapeDetailsPanelVisible else { return }
|
||||
appEnvironment.settingsManager.landscapeDetailsPanelVisible = true
|
||||
LoggingService.shared.debug("ExpandedPlayerWindowManager: restored corner panels after fullscreen", category: .player)
|
||||
}
|
||||
|
||||
/// Restores a window that was hidden for PiP mode.
|
||||
/// Call this when returning from PiP to show the player window again.
|
||||
func restoreFromPiP(animated: Bool = true) {
|
||||
@@ -880,8 +963,27 @@ extension ExpandedPlayerWindowManager: NSWindowDelegate {
|
||||
return false
|
||||
}
|
||||
|
||||
nonisolated func windowWillEnterFullScreen(_: Notification) {
|
||||
MainActor.assumeIsolated {
|
||||
isPlayerWindowEnteringFullScreen = true
|
||||
hideCornerPanelsForFullScreen()
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func windowDidEnterFullScreen(_: Notification) {
|
||||
MainActor.assumeIsolated {
|
||||
isPlayerWindowEnteringFullScreen = false
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func windowDidExitFullScreen(_ notification: Notification) {
|
||||
MainActor.assumeIsolated {
|
||||
isPlayerWindowEnteringFullScreen = false
|
||||
// Mirror of the enter order: the window is back at its size
|
||||
// first, then the panels slide in. Restoring flips
|
||||
// `isCornerPanelLayoutActive` back on, so the aspect-ratio replay
|
||||
// below is skipped (the corner layout then unlocks the ratio).
|
||||
restoreCornerPanelsAfterFullScreen()
|
||||
guard let window = playerWindow else { return }
|
||||
// Restore the pinned (floating) config that toggleFullScreen
|
||||
// dropped so the window could enter primary fullscreen.
|
||||
|
||||
Reference in New Issue
Block a user