diff --git a/Yattee/Services/Navigation/NavigationCoordinator.swift b/Yattee/Services/Navigation/NavigationCoordinator.swift index 6a5c05f4..cc0e9ba1 100644 --- a/Yattee/Services/Navigation/NavigationCoordinator.swift +++ b/Yattee/Services/Navigation/NavigationCoordinator.swift @@ -162,6 +162,11 @@ final class NavigationCoordinator { /// inside it must not start the sheet dismiss pan (drag handle conflict). var cornerBelowPanelFrame: 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). + var panelResizeGrabberFrame: CGRect = .zero + /// Progress bar frame in screen coordinates (for gesture conflict resolution). var progressBarFrame: CGRect = .zero diff --git a/Yattee/Views/Player/ExpandedPlayerWindow.swift b/Yattee/Views/Player/ExpandedPlayerWindow.swift index 14d98fe6..bec6dc8a 100644 --- a/Yattee/Views/Player/ExpandedPlayerWindow.swift +++ b/Yattee/Views/Player/ExpandedPlayerWindow.swift @@ -146,6 +146,8 @@ 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 the panel resize grabber strip frame in screen coordinates (blocks dismiss pan) + var getPanelResizeGrabberFrame: (() -> CGRect)? /// Returns whether the portrait panel is currently visible (not hidden off-screen) var isPortraitPanelVisible: (() -> Bool)? /// Returns the progress bar frame in screen coordinates (for gesture conflict resolution) @@ -378,6 +380,16 @@ private final class DragToDismissGestureHandler: NSObject, UIGestureRecognizerDe } } + // Don't begin dismiss gesture from the panel resize grabber strip — + // a diagonal resize drag would otherwise be cancelled mid-resize + let grabberFrame = getPanelResizeGrabberFrame?() ?? .zero + if !grabberFrame.isEmpty { + let touchLocation = panGesture.location(in: nil) + if grabberFrame.contains(touchLocation) { + return false + } + } + // When portrait panel is visible, only allow dismiss from player area (above panel) // This prevents race conditions where isPanelDragging isn't set yet when gesture begins let panelFrame = getPortraitPanelFrame?() ?? .zero @@ -860,6 +872,9 @@ final class ExpandedPlayerWindowManager { handler.getCornerBelowPanelFrame = { [weak self] in self?.appEnvironment?.navigationCoordinator.cornerBelowPanelFrame ?? .zero } + handler.getPanelResizeGrabberFrame = { [weak self] in + self?.appEnvironment?.navigationCoordinator.panelResizeGrabberFrame ?? .zero + } handler.isPortraitPanelVisible = { [weak self] in self?.appEnvironment?.navigationCoordinator.isPortraitPanelVisible ?? true } diff --git a/Yattee/Views/Player/Panels/PanelResizeGrabber.swift b/Yattee/Views/Player/Panels/PanelResizeGrabber.swift index 92bfcc0d..7e17b8ac 100644 --- a/Yattee/Views/Player/Panels/PanelResizeGrabber.swift +++ b/Yattee/Views/Player/Panels/PanelResizeGrabber.swift @@ -11,6 +11,8 @@ import SwiftUI /// A draggable handle for resizing the panel width. struct PanelResizeGrabber: View { + @Environment(\.appEnvironment) private var appEnvironment + @Binding var panelWidth: CGFloat let minWidth: CGFloat let maxWidth: CGFloat @@ -54,6 +56,22 @@ struct PanelResizeGrabber: View { .frame(width: 4, height: 36) } .gesture(dragGesture) + #if os(iOS) + // Publish the strip frame (with slop) so the window-level dismiss + // pan never begins from a resize drag — a diagonal start would + // otherwise cancel the resize and dismiss the player instead + .background { + GeometryReader { proxy in + Color.clear + .onChange(of: proxy.frame(in: .global), initial: true) { _, frame in + appEnvironment?.navigationCoordinator.panelResizeGrabberFrame = frame.insetBy(dx: -8, dy: 0) + } + .onDisappear { + appEnvironment?.navigationCoordinator.panelResizeGrabberFrame = .zero + } + } + } + #endif #if os(macOS) .onHover { hovering in isHovering = hovering