Allow pinch-to-panscan on the video in the corner layout

The window-level pinch was blocked whenever the landscape panel was
pinned and visible (the corner layout always counts as pinned) or a
stale portrait-panel flag was set. In the corner layout the pinch now
starts when it begins inside the video region, and the panscan
control button is enabled there too.
This commit is contained in:
Arkadiusz Fal
2026-10-20 23:43:47 +02:00
parent b98cc216ad
commit e276c99d0b
3 changed files with 37 additions and 1 deletions

View File

@@ -145,6 +145,10 @@ final class NavigationCoordinator {
/// inside it must not start the sheet dismiss pan (drag handle conflict). /// inside it must not start the sheet dismiss pan (drag handle conflict).
var cornerBelowPanelFrame: CGRect = .zero var cornerBelowPanelFrame: CGRect = .zero
/// Corner layout video region frame in screen coordinates — the pinch
/// panscan gesture only begins inside it while the panels are showing.
var cornerVideoFrame: CGRect = .zero
/// Panel resize grabber strip frame in screen coordinates — touches inside /// Panel resize grabber strip frame in screen coordinates — touches inside
/// it must not start the sheet dismiss pan (a diagonal resize drag would /// it must not start the sheet dismiss pan (a diagonal resize drag would
/// otherwise be cancelled and dismiss the player instead). /// otherwise be cancelled and dismiss the player instead).

View File

@@ -1373,6 +1373,19 @@ extension ExpandedPlayerSheet {
fullWidth: width, fullWidth: width,
fullHeight: height fullHeight: height
) )
#if os(iOS)
.background {
GeometryReader { proxy in
Color.clear
.onChange(of: proxy.frame(in: .global), initial: true) { _, frame in
navigationCoordinator?.cornerVideoFrame = frame
}
.onDisappear {
navigationCoordinator?.cornerVideoFrame = .zero
}
}
}
#endif
}, },
onChannelTap: video.author.hasRealChannelInfo ? { onChannelTap: video.author.hasRealChannelInfo ? {
navigationCoordinator?.navigateToChannel(for: video, collapsePlayer: true) navigationCoordinator?.navigateToChannel(for: video, collapsePlayer: true)
@@ -1686,7 +1699,10 @@ extension ExpandedPlayerSheet {
playerService.selectEmbeddedSubtitleTrack(trackID) playerService.selectEmbeddedSubtitleTrack(trackID)
}, },
panscanValue: navigationCoordinator?.pinchPanscan ?? 0.0, panscanValue: navigationCoordinator?.pinchPanscan ?? 0.0,
isPanscanAllowed: !(isPanelPinned && isPanelVisible), // Corner layout: the video owns its own region, so it can
// fill it even with the panels showing
isPanscanAllowed: !(isPanelPinned && isPanelVisible)
|| appEnvironment?.navigationCoordinator.isCornerLayoutActive == true,
onTogglePanscan: { [weak navigationCoordinator] in onTogglePanscan: { [weak navigationCoordinator] in
navigationCoordinator?.togglePanscan() navigationCoordinator?.togglePanscan()
}, },

View File

@@ -180,6 +180,10 @@ private final class DragToDismissGestureHandler: NSObject, UIGestureRecognizerDe
var getPortraitPanelFrame: (() -> CGRect)? var getPortraitPanelFrame: (() -> CGRect)?
/// Returns the corner layout below-video panel frame in screen coordinates (blocks dismiss pan) /// Returns the corner layout below-video panel frame in screen coordinates (blocks dismiss pan)
var getCornerBelowPanelFrame: (() -> CGRect)? var getCornerBelowPanelFrame: (() -> CGRect)?
/// Returns true while the corner layout is active (its panels don't block pinch)
var isCornerLayoutActive: (() -> Bool)?
/// Returns the corner layout video region frame in screen coordinates (pinch must start inside it)
var getCornerVideoFrame: (() -> CGRect)?
/// Returns the panel resize grabber strip frame in screen coordinates (blocks dismiss pan) /// Returns the panel resize grabber strip frame in screen coordinates (blocks dismiss pan)
var getPanelResizeGrabberFrame: (() -> CGRect)? var getPanelResizeGrabberFrame: (() -> CGRect)?
var getPlayerSideSliderFrames: (() -> [CGRect])? var getPlayerSideSliderFrames: (() -> [CGRect])?
@@ -356,6 +360,12 @@ private final class DragToDismissGestureHandler: NSObject, UIGestureRecognizerDe
// Don't allow pinch gesture when pinned panel is visible on screen or seek gesture is active // Don't allow pinch gesture when pinned panel is visible on screen or seek gesture is active
if gestureRecognizer is UIPinchGestureRecognizer { if gestureRecognizer is UIPinchGestureRecognizer {
if isSeekGestureActive?() == true { return false } if isSeekGestureActive?() == true { return false }
// Corner layout: the video sits in its own region beside the
// panels, so pinching it is fine — but not over the panels
if isCornerLayoutActive?() == true {
let videoFrame = getCornerVideoFrame?() ?? .zero
return videoFrame.isEmpty || videoFrame.contains(gestureRecognizer.location(in: nil))
}
if isPortraitPanelVisible?() == true { return false } if isPortraitPanelVisible?() == true { return false }
return !(isPanelPinnedAndVisible?() ?? false) return !(isPanelPinnedAndVisible?() ?? false)
} }
@@ -883,6 +893,12 @@ final class ExpandedPlayerWindowManager {
handler.getCornerBelowPanelFrame = { [weak self] in handler.getCornerBelowPanelFrame = { [weak self] in
self?.appEnvironment?.navigationCoordinator.cornerBelowPanelFrame ?? .zero self?.appEnvironment?.navigationCoordinator.cornerBelowPanelFrame ?? .zero
} }
handler.isCornerLayoutActive = { [weak self] in
self?.appEnvironment?.navigationCoordinator.isCornerLayoutActive ?? false
}
handler.getCornerVideoFrame = { [weak self] in
self?.appEnvironment?.navigationCoordinator.cornerVideoFrame ?? .zero
}
handler.getPanelResizeGrabberFrame = { [weak self] in handler.getPanelResizeGrabberFrame = { [weak self] in
self?.appEnvironment?.navigationCoordinator.panelResizeGrabberFrame ?? .zero self?.appEnvironment?.navigationCoordinator.panelResizeGrabberFrame ?? .zero
} }