Refactor player controls and improve custom controls visibility

Restructured PlayerControls view hierarchy by extracting controls content into a separate computed property for better code organization. Added shouldShowCustomControls property to VideoPlayerView to properly determine when custom controls should be shown vs system controls. Updated hover logic to only show/hide custom controls when appropriate.
This commit is contained in:
Arkadiusz Fal
2025-11-14 18:58:28 +01:00
parent b8cde410c5
commit 8f97c40257
2 changed files with 182 additions and 172 deletions

View File

@@ -57,8 +57,33 @@ struct PlayerControls: View {
}
var body: some View {
ZStack(alignment: .topLeading) {
Group {
if showControls {
controlsContent
}
}
.onChange(of: model.presentingOverlays) { newValue in
if newValue {
model.hide()
}
}
#if os(tvOS)
.onReceive(model.reporter) { value in
guard player.presentingPlayer else { return }
if value == "swipe down", !model.presentingControls, !model.presentingOverlays {
withAnimation(Self.animation) {
controlsOverlayModel.hide()
}
} else {
model.show()
}
model.resetTimer()
}
#endif
}
var controlsContent: some View {
ZStack(alignment: .topLeading) {
Seek()
.zIndex(4)
.transition(.opacity)
@@ -73,7 +98,6 @@ struct PlayerControls: View {
#else
.offset(y: 2)
#endif
}
VStack {
ZStack {
@@ -87,7 +111,6 @@ struct PlayerControls: View {
}
.offset(y: playerControlsLayout.osdVerticalOffset + 5)
if showControls {
Section {
#if !os(tvOS)
HStack {
@@ -190,7 +213,6 @@ struct PlayerControls: View {
.opacity(model.presentingControls && !player.availableStreams.isEmpty ? 1 : 0)
}
}
}
.frame(maxWidth: .infinity)
#if os(tvOS)
.onChange(of: model.presentingControls) { newValue in
@@ -215,24 +237,6 @@ struct PlayerControls: View {
.frame(maxHeight: .infinity, alignment: .top)
}
}
.onChange(of: model.presentingOverlays) { newValue in
if newValue {
model.hide()
}
}
#if os(tvOS)
.onReceive(model.reporter) { value in
guard player.presentingPlayer else { return }
if value == "swipe down", !model.presentingControls, !model.presentingOverlays {
withAnimation(Self.animation) {
controlsOverlayModel.hide()
}
} else {
model.show()
}
model.resetTimer()
}
#endif
}
var detailsWidth: Double {

View File

@@ -77,6 +77,10 @@ struct VideoPlayerView: View {
@ObservedObject var controlsOverlayModel = ControlOverlaysModel.shared // swiftlint:disable:this swiftui_state_private
var shouldShowCustomControls: Bool {
player.activeBackend == .mpv || !avPlayerUsesSystemControls || player.musicMode
}
var body: some View {
ZStack(alignment: overlayAlignment) {
videoPlayer
@@ -245,7 +249,7 @@ struct VideoPlayerView: View {
var content: some View {
Group {
ZStack(alignment: .bottomLeading) {
ZStack(alignment: .topLeading) {
#if os(tvOS)
ZStack {
player.playerBackendView
@@ -257,6 +261,7 @@ struct VideoPlayerView: View {
.ignoresSafeArea()
#else
GeometryReader { geometry in
VStack(spacing: 0) {
player.playerBackendView
.modifier(
VideoPlayerSizeModifier(
@@ -268,18 +273,21 @@ struct VideoPlayerView: View {
)
.onHover { hovering in
hoveringPlayer = hovering
// Only show/hide custom controls if they should be used
if shouldShowCustomControls {
if hovering {
player.controls.show()
} else {
player.controls.hide()
}
}
}
.gesture(player.controls.presentingOverlays ? nil : playerDragGesture)
#if os(macOS)
.onAppear {
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
hoverThrottle.execute {
if !player.currentItem.isNil, hoveringPlayer {
if !player.currentItem.isNil, hoveringPlayer, shouldShowCustomControls {
player.controls.resetTimer()
}
}
@@ -297,10 +305,7 @@ struct VideoPlayerView: View {
fullScreen: $fullScreenDetails,
sidebarQueue: $sidebarQueue
)
.modifier(VideoDetailsPaddingModifier(
playerSize: player.playerSize,
fullScreen: fullScreenDetails
))
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
#if os(macOS)
// TODO: Check whether this is needed on macOS.
.onDisappear {
@@ -317,6 +322,7 @@ struct VideoPlayerView: View {
VStack {}
}
}
}
#endif
}
#if os(iOS)