mirror of
https://github.com/yattee/yattee.git
synced 2025-08-05 18:24:02 +00:00
tvOS fixes
This commit is contained in:
@@ -10,6 +10,14 @@ struct PlayerControls: View {
|
||||
|
||||
#if os(iOS)
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
#elseif os(tvOS)
|
||||
enum Field: Hashable {
|
||||
case play
|
||||
case backward
|
||||
case forward
|
||||
}
|
||||
|
||||
@FocusState private var focusedField: Field?
|
||||
#endif
|
||||
|
||||
init(player: PlayerModel) {
|
||||
@@ -57,14 +65,27 @@ struct PlayerControls: View {
|
||||
}
|
||||
.opacity(model.presentingControls ? 1 : 0)
|
||||
}
|
||||
.background(controlsBackground)
|
||||
.environment(\.colorScheme, .dark)
|
||||
#if os(tvOS)
|
||||
.onChange(of: model.presentingControls) { _ in
|
||||
if model.presentingControls {
|
||||
focusedField = .play
|
||||
}
|
||||
}
|
||||
.onChange(of: focusedField) { _ in
|
||||
model.resetTimer()
|
||||
}
|
||||
#else
|
||||
.background(controlsBackground)
|
||||
#endif
|
||||
.environment(\.colorScheme, .dark)
|
||||
}
|
||||
|
||||
var controlsBackground: some View {
|
||||
PlayerGestures()
|
||||
.background(Color.black.opacity(model.presentingControls ? 0.5 : 0))
|
||||
}
|
||||
#if !os(tvOS)
|
||||
var controlsBackground: some View {
|
||||
PlayerGestures()
|
||||
.background(Color.black.opacity(model.presentingControls ? 0.5 : 0))
|
||||
}
|
||||
#endif
|
||||
|
||||
var timeline: some View {
|
||||
TimelineView(duration: durationBinding, current: currentTimeBinding, cornerRadius: 0)
|
||||
@@ -93,11 +114,16 @@ struct PlayerControls: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
ToggleBackendButton()
|
||||
Text("•")
|
||||
StreamControl()
|
||||
#if os(macOS)
|
||||
.frame(maxWidth: 160)
|
||||
#if !os(tvOS)
|
||||
ToggleBackendButton()
|
||||
Text("•")
|
||||
|
||||
StreamControl()
|
||||
#if os(macOS)
|
||||
.frame(maxWidth: 160)
|
||||
#endif
|
||||
#else
|
||||
Text(player.stream?.description ?? "")
|
||||
#endif
|
||||
}
|
||||
.foregroundColor(.primary)
|
||||
@@ -111,7 +137,9 @@ struct PlayerControls: View {
|
||||
} label: {
|
||||
Image(systemName: "chevron.down.circle.fill")
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.keyboardShortcut(.cancelAction)
|
||||
#endif
|
||||
}
|
||||
|
||||
private var playbackStatus: String {
|
||||
@@ -146,7 +174,9 @@ struct PlayerControls: View {
|
||||
|
||||
var buttonsBar: some View {
|
||||
HStack {
|
||||
fullscreenButton
|
||||
#if !os(tvOS)
|
||||
fullscreenButton
|
||||
#endif
|
||||
Spacer()
|
||||
// button("Music Mode", systemImage: "music.note")
|
||||
}
|
||||
@@ -159,15 +189,26 @@ struct PlayerControls: View {
|
||||
) {
|
||||
model.toggleFullscreen(fullScreenLayout)
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.keyboardShortcut(fullScreenLayout ? .cancelAction : .defaultAction)
|
||||
#endif
|
||||
}
|
||||
|
||||
var mediumButtonsBar: some View {
|
||||
HStack {
|
||||
button("Seek Backward", systemImage: "gobackward.10", size: 50, cornerRadius: 10) {
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||
}
|
||||
.keyboardShortcut("k")
|
||||
#if !os(tvOS)
|
||||
button("Seek Backward", systemImage: "gobackward.10", size: 50, cornerRadius: 10) {
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
.focused($focusedField, equals: .backward)
|
||||
#else
|
||||
.keyboardShortcut("k")
|
||||
.keyboardShortcut(.leftArrow)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -179,15 +220,27 @@ struct PlayerControls: View {
|
||||
) {
|
||||
player.backend.togglePlay()
|
||||
}
|
||||
#if os(tvOS)
|
||||
.focused($focusedField, equals: .play)
|
||||
#else
|
||||
.keyboardShortcut("p")
|
||||
.keyboardShortcut(.space)
|
||||
#endif
|
||||
.disabled(model.isLoadingVideo)
|
||||
|
||||
Spacer()
|
||||
|
||||
button("Seek Forward", systemImage: "goforward.10", size: 50, cornerRadius: 10) {
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||
}
|
||||
.keyboardShortcut("l")
|
||||
#if !os(tvOS)
|
||||
button("Seek Forward", systemImage: "goforward.10", size: 50, cornerRadius: 10) {
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||
}
|
||||
#if os(tvOS)
|
||||
.focused($focusedField, equals: .forward)
|
||||
#else
|
||||
.keyboardShortcut("l")
|
||||
.keyboardShortcut(.rightArrow)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
.font(.system(size: 30))
|
||||
.padding(.horizontal, 4)
|
||||
@@ -234,7 +287,7 @@ struct PlayerControls: View {
|
||||
}
|
||||
|
||||
var fullScreenLayout: Bool {
|
||||
#if !os(macOS)
|
||||
#if os(iOS)
|
||||
model.playingFullscreen || verticalSizeClass == .compact
|
||||
#else
|
||||
model.playingFullscreen
|
||||
|
@@ -47,6 +47,7 @@ struct TimelineView: View {
|
||||
|
||||
.frame(maxHeight: height * 2)
|
||||
|
||||
#if !os(tvOS)
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0)
|
||||
.onChanged { value in
|
||||
@@ -79,6 +80,7 @@ struct TimelineView: View {
|
||||
controls.resetTimer()
|
||||
}
|
||||
)
|
||||
#endif
|
||||
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
@@ -100,11 +102,13 @@ struct TimelineView: View {
|
||||
self.size = size
|
||||
}
|
||||
})
|
||||
#if !os(tvOS)
|
||||
.gesture(DragGesture(minimumDistance: 0).onEnded { value in
|
||||
let target = (value.location.x / size.width) * units
|
||||
current = target
|
||||
player.backend.seek(to: target)
|
||||
})
|
||||
#endif
|
||||
}
|
||||
|
||||
var projectedValue: Double {
|
||||
|
@@ -89,7 +89,7 @@ struct VideoDetails: View {
|
||||
if fullScreen {
|
||||
fullScreen = false
|
||||
} else {
|
||||
self.presentationMode.wrappedValue.dismiss()
|
||||
self.player.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -93,8 +93,26 @@ struct VideoPlayerView: View {
|
||||
Group {
|
||||
Group {
|
||||
#if os(tvOS)
|
||||
player.playerView
|
||||
playerView
|
||||
.ignoresSafeArea(.all, edges: .all)
|
||||
.onMoveCommand { direction in
|
||||
if direction == .left {
|
||||
playerControls.resetTimer()
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||
}
|
||||
if direction == .right {
|
||||
playerControls.resetTimer()
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||
}
|
||||
if direction == .up {
|
||||
playerControls.show()
|
||||
playerControls.resetTimer()
|
||||
}
|
||||
if direction == .down {
|
||||
playerControls.show()
|
||||
playerControls.resetTimer()
|
||||
}
|
||||
}
|
||||
#else
|
||||
GeometryReader { geometry in
|
||||
VStack(spacing: 0) {
|
||||
@@ -103,30 +121,8 @@ struct VideoPlayerView: View {
|
||||
} else if player.playingInPictureInPicture {
|
||||
pictureInPicturePlaceholder(geometry: geometry)
|
||||
} else {
|
||||
ZStack(alignment: .top) {
|
||||
switch player.activeBackend {
|
||||
case .mpv:
|
||||
player.mpvPlayerView
|
||||
.overlay(GeometryReader { proxy in
|
||||
Color.clear
|
||||
.onAppear {
|
||||
player.playerSize = proxy.size
|
||||
// TODO: move to backend method
|
||||
player.mpvBackend.client?.setSize(proxy.size.width, proxy.size.height)
|
||||
}
|
||||
.onChange(of: proxy.size) { _ in
|
||||
player.playerSize = proxy.size
|
||||
player.mpvBackend.client?.setSize(proxy.size.width, proxy.size.height)
|
||||
}
|
||||
})
|
||||
case .appleAVPlayer:
|
||||
player.avPlayerView
|
||||
}
|
||||
|
||||
PlayerGestures()
|
||||
|
||||
PlayerControls(player: player)
|
||||
}
|
||||
playerView
|
||||
#if !os(tvOS)
|
||||
.modifier(
|
||||
VideoPlayerSizeModifier(
|
||||
geometry: geometry,
|
||||
@@ -134,6 +130,7 @@ struct VideoPlayerView: View {
|
||||
fullScreen: playerControls.playingFullscreen
|
||||
)
|
||||
)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: fullScreenLayout ? .infinity : nil, maxHeight: fullScreenLayout ? .infinity : nil)
|
||||
@@ -165,24 +162,26 @@ struct VideoPlayerView: View {
|
||||
|
||||
.background(Color.black)
|
||||
|
||||
if !playerControls.playingFullscreen {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
if verticalSizeClass == .regular {
|
||||
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
||||
}
|
||||
#if !os(tvOS)
|
||||
if !playerControls.playingFullscreen {
|
||||
Group {
|
||||
#if os(iOS)
|
||||
if verticalSizeClass == .regular {
|
||||
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
||||
}
|
||||
|
||||
#else
|
||||
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
||||
#endif
|
||||
#else
|
||||
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreenDetails)
|
||||
#endif
|
||||
}
|
||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||
.modifier(VideoDetailsPaddingModifier(
|
||||
geometry: geometry,
|
||||
aspectRatio: player.avPlayerBackend.controller?.aspectRatio,
|
||||
fullScreen: fullScreenDetails
|
||||
))
|
||||
}
|
||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||
.modifier(VideoDetailsPaddingModifier(
|
||||
geometry: geometry,
|
||||
aspectRatio: player.avPlayerBackend.controller?.aspectRatio,
|
||||
fullScreen: fullScreenDetails
|
||||
))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -205,14 +204,40 @@ struct VideoPlayerView: View {
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea(.all, edges: fullScreenLayout ? .vertical : Edge.Set())
|
||||
#if !os(macOS)
|
||||
#if os(iOS)
|
||||
.statusBar(hidden: playerControls.playingFullscreen)
|
||||
.navigationBarHidden(true)
|
||||
#endif
|
||||
}
|
||||
|
||||
var playerView: some View {
|
||||
ZStack(alignment: .top) {
|
||||
switch player.activeBackend {
|
||||
case .mpv:
|
||||
player.mpvPlayerView
|
||||
.overlay(GeometryReader { proxy in
|
||||
Color.clear
|
||||
.onAppear {
|
||||
player.playerSize = proxy.size
|
||||
}
|
||||
.onChange(of: proxy.size) { _ in
|
||||
player.playerSize = proxy.size
|
||||
}
|
||||
})
|
||||
case .appleAVPlayer:
|
||||
player.avPlayerView
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
PlayerGestures()
|
||||
#endif
|
||||
|
||||
PlayerControls(player: player)
|
||||
}
|
||||
}
|
||||
|
||||
var fullScreenLayout: Bool {
|
||||
#if !os(macOS)
|
||||
#if os(iOS)
|
||||
playerControls.playingFullscreen || verticalSizeClass == .compact
|
||||
#else
|
||||
playerControls.playingFullscreen
|
||||
|
Reference in New Issue
Block a user