From e276c99d0b17543b7fcf62e89965cce296475426 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Tue, 20 Oct 2026 23:43:47 +0200 Subject: [PATCH] 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. --- .../Navigation/NavigationCoordinator.swift | 4 ++++ .../Player/ExpandedPlayerSheet+Layouts.swift | 18 +++++++++++++++++- Yattee/Views/Player/ExpandedPlayerWindow.swift | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Yattee/Services/Navigation/NavigationCoordinator.swift b/Yattee/Services/Navigation/NavigationCoordinator.swift index df579e36..a445efea 100644 --- a/Yattee/Services/Navigation/NavigationCoordinator.swift +++ b/Yattee/Services/Navigation/NavigationCoordinator.swift @@ -145,6 +145,10 @@ final class NavigationCoordinator { /// inside it must not start the sheet dismiss pan (drag handle conflict). 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 /// it must not start the sheet dismiss pan (a diagonal resize drag would /// otherwise be cancelled and dismiss the player instead). diff --git a/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift b/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift index 6136682b..776e3029 100644 --- a/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift +++ b/Yattee/Views/Player/ExpandedPlayerSheet+Layouts.swift @@ -1373,6 +1373,19 @@ extension ExpandedPlayerSheet { fullWidth: width, 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 ? { navigationCoordinator?.navigateToChannel(for: video, collapsePlayer: true) @@ -1686,7 +1699,10 @@ extension ExpandedPlayerSheet { playerService.selectEmbeddedSubtitleTrack(trackID) }, 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 navigationCoordinator?.togglePanscan() }, diff --git a/Yattee/Views/Player/ExpandedPlayerWindow.swift b/Yattee/Views/Player/ExpandedPlayerWindow.swift index f837b728..811ac1fc 100644 --- a/Yattee/Views/Player/ExpandedPlayerWindow.swift +++ b/Yattee/Views/Player/ExpandedPlayerWindow.swift @@ -180,6 +180,10 @@ private final class DragToDismissGestureHandler: NSObject, UIGestureRecognizerDe var getPortraitPanelFrame: (() -> CGRect)? /// Returns the corner layout below-video panel frame in screen coordinates (blocks dismiss pan) 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) var getPanelResizeGrabberFrame: (() -> 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 if gestureRecognizer is UIPinchGestureRecognizer { 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 } return !(isPanelPinnedAndVisible?() ?? false) } @@ -883,6 +893,12 @@ final class ExpandedPlayerWindowManager { handler.getCornerBelowPanelFrame = { [weak self] in 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 self?.appEnvironment?.navigationCoordinator.panelResizeGrabberFrame ?? .zero }