Keep player buttons off the hinge in book posture

On a foldable with the hinge partially open in landscape, the fullscreen
player's buttons move to the half right of the hinge. The dimming
background and tap handling still cover the whole screen.
This commit is contained in:
Arkadiusz Fal
2026-10-12 22:03:02 +02:00
parent 23d6a605a0
commit 77c3103748
2 changed files with 25 additions and 1 deletions

View File

@@ -30,6 +30,9 @@ extension ExpandedPlayerSheet {
/// center controls and side sliders clear of both.
static let tabletopTopBarInset: CGFloat = 72
static let tabletopBottomBarInset: CGFloat = 96
/// Book posture (hinge vertical): clearance between the hinge line and
/// the controls confined to the trailing half.
static let bookFoldHingeGap: CGFloat = 16
#if os(iOS)
/// Reflection strength at the hinge line; it fades to nothing at
@@ -1419,6 +1422,21 @@ extension ExpandedPlayerSheet {
// Calculate offset for controls positioning (used for animation)
let controlsOffset = (leadingSafeArea - trailingSafeArea) / 2
#if os(iOS)
// Foldable in book posture (hinge partially open, landscape, the
// player spanning the fold): the buttons move to the trailing half so
// nothing sits on the hinge; dimming and taps still cover the whole
// screen. The corner layout's hinge split with panels shown already
// gives the player one half only.
let bookFoldButtonsInset: CGFloat = {
guard isTabletopHinge,
fullWidth > fullHeight,
fullWidth >= Self.minWidthForCornerLayout else { return 0 }
// Controls frame starts at the leading safe area
return max(0, fullWidth / 2 + Self.bookFoldHingeGap - leadingSafeArea)
}()
#endif
// Calculate video frame size based on panscan
// At panscan 0: aspect-fit (may have black bars)
// At panscan 1: aspect-fill (cropped to fill)
@@ -1665,7 +1683,8 @@ extension ExpandedPlayerSheet {
onTogglePanscan: { [weak navigationCoordinator] in
navigationCoordinator?.togglePanscan()
},
activeLayout: playerControlsLayout
activeLayout: playerControlsLayout,
buttonsLeadingInset: bookFoldButtonsInset
)
.frame(width: availableWidth, height: availableHeight)
.offset(x: controlsOffset)

View File

@@ -149,6 +149,10 @@ struct PlayerControlsView: View {
@Environment(\.statusBarVisibilityController) private var statusBarController
/// Active player controls layout from preset (passed from parent to avoid flashing during view recreation)
var activeLayout: PlayerControlsLayout = .default
/// Moves the buttons off the leading part of the controls frame while the
/// dimming background and tap handling keep covering all of it (foldable
/// book posture: buttons on the half right of the hinge).
var buttonsLeadingInset: CGFloat = 0
/// Animation trigger for seek backward button
@State private var seekBackwardTrigger = false
/// Animation trigger for seek forward button
@@ -512,6 +516,7 @@ struct PlayerControlsView: View {
// Controls layout - explicitly fill parent bounds
controlsVStack
.padding(.leading, buttonsLeadingInset)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}