mirror of
https://github.com/yattee/yattee.git
synced 2025-11-24 18:28:20 +00:00
In fullscreen playback, swipe-down and timeline seek gestures now respect a 60pt safe zone at the top of the screen, allowing the system notification center gesture to work without triggering app gestures.
175 lines
6.1 KiB
Swift
175 lines
6.1 KiB
Swift
import Defaults
|
|
import SwiftUI
|
|
|
|
extension VideoPlayerView {
|
|
var playerDragGesture: some Gesture {
|
|
DragGesture(minimumDistance: 30, coordinateSpace: .global)
|
|
#if os(iOS)
|
|
.updating($dragGestureOffset) { value, state, _ in
|
|
guard isVerticalDrag else { return }
|
|
var translation = value.translation
|
|
translation.height = max(-translation.height, translation.height)
|
|
state = translation
|
|
}
|
|
#endif
|
|
.updating($dragGestureState) { _, state, _ in
|
|
state = true
|
|
}
|
|
.onChanged { value in
|
|
#if os(iOS)
|
|
// In fullscreen, ignore gestures that start in the top notification center area
|
|
// to allow system notification center gesture to work
|
|
if player.playingFullScreen {
|
|
if value.startLocation.y < Constants.notificationCenterZoneHeight {
|
|
return
|
|
}
|
|
}
|
|
#endif
|
|
|
|
guard player.presentingPlayer,
|
|
!controlsOverlayModel.presenting,
|
|
dragGestureState,
|
|
!disableToggleGesture else { return }
|
|
|
|
if player.controls.presentingControls, !player.musicMode {
|
|
player.controls.presentingControls = false
|
|
}
|
|
|
|
if player.musicMode {
|
|
player.backend.stopControlsUpdates()
|
|
}
|
|
|
|
let verticalDrag = value.translation.height
|
|
let horizontalDrag = value.translation.width
|
|
|
|
#if os(iOS)
|
|
if viewDragOffset > 0, !isVerticalDrag {
|
|
isVerticalDrag = true
|
|
}
|
|
#endif
|
|
|
|
if !isVerticalDrag,
|
|
horizontalPlayerGestureEnabled,
|
|
abs(horizontalDrag) > seekGestureSensitivity,
|
|
!isHorizontalDrag,
|
|
player.activeBackend == .mpv || !avPlayerUsesSystemControls
|
|
{
|
|
isHorizontalDrag = true
|
|
player.seek.onSeekGestureStart()
|
|
viewDragOffset = 0
|
|
}
|
|
|
|
if horizontalPlayerGestureEnabled, isHorizontalDrag {
|
|
player.seek.updateCurrentTime {
|
|
let time = player.backend.playerItemDuration?.seconds ?? 0
|
|
if player.seek.gestureStart.isNil {
|
|
player.seek.gestureStart = time
|
|
}
|
|
let timeSeek = (time / player.playerSize.width) * horizontalDrag * seekGestureSpeed
|
|
player.seek.gestureSeek = timeSeek
|
|
}
|
|
return
|
|
}
|
|
|
|
// Toggle fullscreen on upward drag only when not disabled
|
|
if fullscreenPlayerGestureEnabled, verticalDrag < -50 {
|
|
player.toggleFullScreenAction()
|
|
disableGestureTemporarily()
|
|
return
|
|
}
|
|
|
|
// Ignore downward swipes when in fullscreen
|
|
guard verticalDrag > 0 && !player.playingFullScreen else {
|
|
return
|
|
}
|
|
viewDragOffset = verticalDrag
|
|
}
|
|
.onEnded { _ in
|
|
onPlayerDragGestureEnded()
|
|
}
|
|
}
|
|
|
|
var detailsDragGesture: some Gesture {
|
|
DragGesture(minimumDistance: 30)
|
|
.onChanged { value in
|
|
handleDetailsDragChange(value)
|
|
}
|
|
.onEnded { value in
|
|
handleDetailsDragEnd(value)
|
|
}
|
|
}
|
|
|
|
private func handleDetailsDragChange(_ value: DragGesture.Value) {
|
|
let maxOffset = -player.playerSize.height
|
|
|
|
// Continuous drag update for smooth movement of VideoDetails
|
|
if fullScreenDetails {
|
|
// Allow only downward dragging when in fullscreen
|
|
if value.translation.height > 0 {
|
|
detailViewDragOffset = min(value.translation.height, abs(maxOffset))
|
|
}
|
|
} else {
|
|
// Allow only upward dragging when not in fullscreen
|
|
if value.translation.height < 0 {
|
|
detailViewDragOffset = max(value.translation.height, maxOffset)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func handleDetailsDragEnd(_ value: DragGesture.Value) {
|
|
if value.translation.height < -50, !fullScreenDetails {
|
|
// Swipe up to enter fullscreen
|
|
withAnimation(Constants.overlayAnimation) {
|
|
fullScreenDetails = true
|
|
detailViewDragOffset = 0
|
|
}
|
|
} else if value.translation.height > 50, fullScreenDetails {
|
|
// Swipe down to exit fullscreen
|
|
withAnimation(Constants.overlayAnimation) {
|
|
fullScreenDetails = false
|
|
detailViewDragOffset = 0
|
|
}
|
|
} else {
|
|
// Reset offset if drag was not significant
|
|
withAnimation(Constants.overlayAnimation) {
|
|
detailViewDragOffset = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
func onPlayerDragGestureEnded() {
|
|
if horizontalPlayerGestureEnabled, isHorizontalDrag {
|
|
isHorizontalDrag = false
|
|
player.seek.onSeekGestureEnd()
|
|
}
|
|
|
|
isVerticalDrag = false
|
|
|
|
guard player.presentingPlayer,
|
|
!controlsOverlayModel.presenting else { return }
|
|
|
|
if viewDragOffset > 100 {
|
|
withAnimation(Constants.overlayAnimation) {
|
|
viewDragOffset = Self.hiddenOffset
|
|
}
|
|
} else {
|
|
withAnimation(Constants.overlayAnimation) {
|
|
viewDragOffset = 0
|
|
}
|
|
player.backend.setNeedsDrawing(true)
|
|
player.show()
|
|
|
|
if player.musicMode {
|
|
player.backend.startControlsUpdates()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func disableGestureTemporarily() {
|
|
disableToggleGesture = true
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
|
disableToggleGesture = false
|
|
}
|
|
}
|
|
}
|