New actions buttons

This commit is contained in:
Arkadiusz Fal 2023-04-22 16:33:08 +02:00
parent a7763c5802
commit 8f9fb7ba82
7 changed files with 132 additions and 34 deletions

View File

@ -664,6 +664,46 @@ final class PlayerModel: ObservableObject {
backend.closePiP() backend.closePiP()
} }
var pipImage: String {
transitioningToPiP ? "pip.fill" : pipController?.isPictureInPictureActive ?? false ? "pip.exit" : "pip.enter"
}
var fullscreenImage: String {
playingFullScreen ? "arrow.down.right.and.arrow.up.left" : "arrow.up.left.and.arrow.down.right"
}
func toggleFullScreenAction() {
toggleFullscreen(playingFullScreen, showControls: false)
}
func togglePiPAction() {
(pipController?.isPictureInPictureActive ?? false) ? closePiP() : startPiP()
}
#if os(iOS)
var lockOrientationImage: String {
lockedOrientation.isNil ? "lock.rotation.open" : "lock.rotation"
}
func lockOrientationAction() {
if lockedOrientation.isNil {
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
lockedOrientation = orientationMask
let orientation = OrientationTracker.shared.currentInterfaceOrientation
Orientation.lockOrientation(orientationMask, andRotateTo: .landscapeLeft)
// iOS 16 workaround
Orientation.lockOrientation(orientationMask, andRotateTo: orientation)
} else {
lockedOrientation = nil
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
}
}
#endif
func replayAction() {
backend.seek(to: 0.0, seekType: .userInteracted)
}
func handleQueueChange() { func handleQueueChange() {
Defaults[.queue] = queue Defaults[.queue] = queue

View File

@ -201,6 +201,12 @@ extension Defaults.Keys {
static let actionButtonSettingsEnabled = Key<Bool>("actionButtonSettingsEnabled", default: true) static let actionButtonSettingsEnabled = Key<Bool>("actionButtonSettingsEnabled", default: true)
static let actionButtonHideEnabled = Key<Bool>("actionButtonHideEnabled", default: false) static let actionButtonHideEnabled = Key<Bool>("actionButtonHideEnabled", default: false)
static let actionButtonCloseEnabled = Key<Bool>("actionButtonCloseEnabled", default: true) static let actionButtonCloseEnabled = Key<Bool>("actionButtonCloseEnabled", default: true)
static let actionButtonFullScreenEnabled = Key<Bool>("actionButtonFullScreenEnabled", default: false)
static let actionButtonPipEnabled = Key<Bool>("actionButtonPipEnabled", default: false)
static let actionButtonLockOrientationEnabled = Key<Bool>("actionButtonLockOrientationEnabled", default: false)
static let actionButtonRestartEnabled = Key<Bool>("actionButtonRestartEnabled", default: false)
static let actionButtonAdvanceToNextItemEnabled = Key<Bool>("actionButtonAdvanceToNextItemEnabled", default: false)
static let actionButtonMusicModeEnabled = Key<Bool>("actionButtonMusicModeEnabled", default: true)
#if os(iOS) #if os(iOS)
static let playerControlsLockOrientationEnabled = Key<Bool>("playerControlsLockOrientationEnabled", default: true) static let playerControlsLockOrientationEnabled = Key<Bool>("playerControlsLockOrientationEnabled", default: true)
@ -215,7 +221,7 @@ extension Defaults.Keys {
static let playerControlsRestartEnabled = Key<Bool>("playerControlsRestartEnabled", default: false) static let playerControlsRestartEnabled = Key<Bool>("playerControlsRestartEnabled", default: false)
static let playerControlsAdvanceToNextEnabled = Key<Bool>("playerControlsAdvanceToNextEnabled", default: false) static let playerControlsAdvanceToNextEnabled = Key<Bool>("playerControlsAdvanceToNextEnabled", default: false)
static let playerControlsPlaybackModeEnabled = Key<Bool>("playerControlsPlaybackModeEnabled", default: false) static let playerControlsPlaybackModeEnabled = Key<Bool>("playerControlsPlaybackModeEnabled", default: false)
static let playerControlsMusicModeEnabled = Key<Bool>("playerControlsMusicModeEnabled", default: true) static let playerControlsMusicModeEnabled = Key<Bool>("playerControlsMusicModeEnabled", default: false)
static let mpvCacheSecs = Key<String>("mpvCacheSecs", default: "120") static let mpvCacheSecs = Key<String>("mpvCacheSecs", default: "120")
static let mpvCachePauseWait = Key<String>("mpvCachePauseWait", default: "3") static let mpvCachePauseWait = Key<String>("mpvCachePauseWait", default: "3")

View File

@ -329,7 +329,7 @@ struct PlayerControls: View {
var fullscreenButton: some View { var fullscreenButton: some View {
button( button(
"Fullscreen", "Fullscreen",
systemImage: player.playingFullScreen ? "arrow.down.right.and.arrow.up.left" : "arrow.up.left.and.arrow.down.right" systemImage: player.fullscreenImage
) { ) {
player.toggleFullscreen(player.playingFullScreen, showControls: false) player.toggleFullscreen(player.playingFullScreen, showControls: false)
} }
@ -367,28 +367,13 @@ struct PlayerControls: View {
} }
private var pipButton: some View { private var pipButton: some View {
let image = player.transitioningToPiP ? "pip.fill" : player.pipController?.isPictureInPictureActive ?? false ? "pip.exit" : "pip.enter" button("PiP", systemImage: player.pipImage, action: player.togglePiPAction)
return button("PiP", systemImage: image) { .disabled(!player.pipPossible)
(player.pipController?.isPictureInPictureActive ?? false) ? player.closePiP() : player.startPiP()
}
.disabled(!player.pipPossible)
} }
#if os(iOS) #if os(iOS)
private var lockOrientationButton: some View { private var lockOrientationButton: some View {
button("Lock Rotation", systemImage: player.lockedOrientation.isNil ? "lock.rotation.open" : "lock.rotation", active: !player.lockedOrientation.isNil) { button("Lock Rotation", systemImage: player.lockOrientationImage, active: !player.lockedOrientation.isNil, action: player.lockOrientationAction)
if player.lockedOrientation.isNil {
let orientationMask = OrientationTracker.shared.currentInterfaceOrientationMask
player.lockedOrientation = orientationMask
let orientation = OrientationTracker.shared.currentInterfaceOrientation
Orientation.lockOrientation(orientationMask, andRotateTo: .landscapeLeft)
// iOS 16 workaround
Orientation.lockOrientation(orientationMask, andRotateTo: orientation)
} else {
player.lockedOrientation = nil
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
}
}
} }
#endif #endif
@ -456,9 +441,7 @@ struct PlayerControls: View {
} }
private var restartVideoButton: some View { private var restartVideoButton: some View {
button("Restart video", systemImage: "backward.end.fill", cornerRadius: 5) { button("Restart video", systemImage: "backward.end.fill", cornerRadius: 5, action: player.replayAction)
player.backend.seek(to: 0.0, seekType: .userInteracted)
}
} }
private var togglePlayButton: some View { private var togglePlayButton: some View {

View File

@ -6,6 +6,14 @@ struct VideoActions: View {
case share case share
case addToPlaylist case addToPlaylist
case subscribe case subscribe
case fullScreen
case pip
#if os(iOS)
case lockOrientation
#endif
case restart
case advanceToNextItem
case musicMode
case settings case settings
case hide case hide
case close case close
@ -24,6 +32,12 @@ struct VideoActions: View {
@Default(.actionButtonAddToPlaylistEnabled) private var actionButtonAddToPlaylistEnabled @Default(.actionButtonAddToPlaylistEnabled) private var actionButtonAddToPlaylistEnabled
@Default(.actionButtonSubscribeEnabled) private var actionButtonSubscribeEnabled @Default(.actionButtonSubscribeEnabled) private var actionButtonSubscribeEnabled
@Default(.actionButtonSettingsEnabled) private var actionButtonSettingsEnabled @Default(.actionButtonSettingsEnabled) private var actionButtonSettingsEnabled
@Default(.actionButtonFullScreenEnabled) private var actionButtonFullScreenEnabled
@Default(.actionButtonPipEnabled) private var actionButtonPipEnabled
@Default(.actionButtonLockOrientationEnabled) private var actionButtonLockOrientationEnabled
@Default(.actionButtonRestartEnabled) private var actionButtonRestartEnabled
@Default(.actionButtonAdvanceToNextItemEnabled) private var actionButtonAdvanceToNextItemEnabled
@Default(.actionButtonMusicModeEnabled) private var actionButtonMusicModeEnabled
@Default(.actionButtonHideEnabled) private var actionButtonHideEnabled @Default(.actionButtonHideEnabled) private var actionButtonHideEnabled
@Default(.actionButtonCloseEnabled) private var actionButtonCloseEnabled @Default(.actionButtonCloseEnabled) private var actionButtonCloseEnabled
@ -51,6 +65,20 @@ struct VideoActions: View {
return actionButtonSubscribeEnabled return actionButtonSubscribeEnabled
case .settings: case .settings:
return actionButtonSettingsEnabled return actionButtonSettingsEnabled
case .fullScreen:
return actionButtonFullScreenEnabled
case .pip:
return actionButtonPipEnabled
#if os(iOS)
case .lockOrientation:
return actionButtonLockOrientationEnabled
#endif
case .restart:
return actionButtonRestartEnabled
case .advanceToNextItem:
return actionButtonAdvanceToNextItemEnabled
case .musicMode:
return actionButtonMusicModeEnabled
case .hide: case .hide:
return actionButtonHideEnabled return actionButtonHideEnabled
case .close: case .close:
@ -68,6 +96,8 @@ struct VideoActions: View {
return !(video?.isLocal ?? true) && accounts.signedIn && accounts.app.supportsSubscriptions return !(video?.isLocal ?? true) && accounts.signedIn && accounts.app.supportsSubscriptions
case .settings: case .settings:
return video != nil return video != nil
case .advanceToNextItem:
return player.isAdvanceToNextItemAvailable
default: default:
return true return true
} }
@ -110,6 +140,23 @@ struct VideoActions: View {
} }
} }
} }
case .fullScreen:
actionButton("Fullscreen", systemImage: player.fullscreenImage, action: player.toggleFullScreenAction)
case .pip:
actionButton("PiP", systemImage: player.pipImage, action: player.togglePiPAction)
#if os(iOS)
case .lockOrientation:
actionButton("Lock", systemImage: player.lockOrientationImage, active: player.lockedOrientation != nil, action: player.lockOrientationAction)
#endif
case .restart:
actionButton("Replay", systemImage: "backward.end.fill", action: player.replayAction)
case .advanceToNextItem:
actionButton("Next", systemImage: "forward.fill") {
player.advanceToNextItem()
}
case .musicMode:
actionButton("Music", systemImage: "music.note", active: player.musicMode, action: player.toggleMusicMode)
case .settings: case .settings:
actionButton("Settings", systemImage: "gear") { actionButton("Settings", systemImage: "gear") {
withAnimation(ControlOverlaysModel.animation) { withAnimation(ControlOverlaysModel.animation) {
@ -138,15 +185,17 @@ struct VideoActions: View {
func actionButton( func actionButton(
_ name: String, _ name: String,
systemImage: String, systemImage: String,
active: Bool = false,
action: @escaping () -> Void = {} action: @escaping () -> Void = {}
) -> some View { ) -> some View {
Button(action: action) { Button(action: action) {
VStack(spacing: 3) { VStack(spacing: 3) {
Image(systemName: systemImage) Image(systemName: systemImage)
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.foregroundColor(active ? Color("AppRedColor") : .accentColor)
if playerActionsButtonLabelStyle.text { if playerActionsButtonLabelStyle.text {
Text(name.localized()) Text(name.localized())
.foregroundColor(.secondary) .foregroundColor(active ? Color("AppRedColor") : .secondary)
.font(.caption2) .font(.caption2)
.allowsTightening(true) .allowsTightening(true)
} }

View File

@ -257,7 +257,7 @@ struct VideoDetails: View {
switch page { switch page {
case .queue: case .queue:
return !player.queue.isEmpty return !sidebarQueue && player.isAdvanceToNextItemAvailable
default: default:
return !video.isLocal return !video.isLocal
} }

View File

@ -18,6 +18,12 @@ struct PlayerControlsSettings: View {
@Default(.actionButtonCloseEnabled) private var actionButtonCloseEnabled @Default(.actionButtonCloseEnabled) private var actionButtonCloseEnabled
@Default(.actionButtonAddToPlaylistEnabled) private var actionButtonAddToPlaylistEnabled @Default(.actionButtonAddToPlaylistEnabled) private var actionButtonAddToPlaylistEnabled
@Default(.actionButtonSettingsEnabled) private var actionButtonSettingsEnabled @Default(.actionButtonSettingsEnabled) private var actionButtonSettingsEnabled
@Default(.actionButtonFullScreenEnabled) private var actionButtonFullScreenEnabled
@Default(.actionButtonPipEnabled) private var actionButtonPipEnabled
@Default(.actionButtonLockOrientationEnabled) private var actionButtonLockOrientationEnabled
@Default(.actionButtonRestartEnabled) private var actionButtonRestartEnabled
@Default(.actionButtonAdvanceToNextItemEnabled) private var actionButtonAdvanceToNextItemEnabled
@Default(.actionButtonMusicModeEnabled) private var actionButtonMusicModeEnabled
@Default(.actionButtonHideEnabled) private var actionButtonHideEnabled @Default(.actionButtonHideEnabled) private var actionButtonHideEnabled
#if os(iOS) #if os(iOS)
@ -211,6 +217,7 @@ struct PlayerControlsSettings: View {
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
.padding(7) .padding(7)
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
.accessibilityAddTraits(.isButton)
#if os(iOS) #if os(iOS)
.background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor)) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor))
#endif #endif
@ -246,6 +253,7 @@ struct PlayerControlsSettings: View {
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
.padding(7) .padding(7)
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
.accessibilityAddTraits(.isButton)
#if os(iOS) #if os(iOS)
.frame(minHeight: 35) .frame(minHeight: 35)
.background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor)) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor))
@ -265,12 +273,24 @@ struct PlayerControlsSettings: View {
} }
@ViewBuilder private var actionButtonToggles: some View { @ViewBuilder private var actionButtonToggles: some View {
Toggle("Share", isOn: $actionButtonShareEnabled) Group {
Toggle("Add to Playlist", isOn: $actionButtonAddToPlaylistEnabled) Toggle("Share", isOn: $actionButtonShareEnabled)
Toggle("Subscribe/Unsubscribe", isOn: $actionButtonSubscribeEnabled) Toggle("Add to Playlist", isOn: $actionButtonAddToPlaylistEnabled)
Toggle("Settings", isOn: $actionButtonSettingsEnabled) Toggle("Subscribe/Unsubscribe", isOn: $actionButtonSubscribeEnabled)
Toggle("Hide player", isOn: $actionButtonHideEnabled) Toggle("Settings", isOn: $actionButtonSettingsEnabled)
Toggle("Close video", isOn: $actionButtonCloseEnabled) Toggle("Fullscreen", isOn: $actionButtonFullScreenEnabled)
Toggle("Picture in Picture", isOn: $actionButtonPipEnabled)
}
Group {
#if os(iOS)
Toggle("Lock orientation", isOn: $actionButtonLockOrientationEnabled)
#endif
Toggle("Restart", isOn: $actionButtonRestartEnabled)
Toggle("Play next item", isOn: $actionButtonAdvanceToNextItemEnabled)
Toggle("Music Mode", isOn: $actionButtonMusicModeEnabled)
Toggle("Hide player", isOn: $actionButtonHideEnabled)
Toggle("Close video", isOn: $actionButtonCloseEnabled)
}
} }
@ViewBuilder private var controlButtonToggles: some View { @ViewBuilder private var controlButtonToggles: some View {
@ -283,9 +303,9 @@ struct PlayerControlsSettings: View {
#endif #endif
Toggle("Restart", isOn: $playerControlsRestartEnabled) Toggle("Restart", isOn: $playerControlsRestartEnabled)
Toggle("Play next item", isOn: $playerControlsAdvanceToNextEnabled) Toggle("Play next item", isOn: $playerControlsAdvanceToNextEnabled)
Toggle("Playback mode", isOn: $playerControlsPlaybackModeEnabled) Toggle("Playback Mode", isOn: $playerControlsPlaybackModeEnabled)
#if !os(tvOS) #if !os(tvOS)
Toggle("Music mode", isOn: $playerControlsMusicModeEnabled) Toggle("Music Mode", isOn: $playerControlsMusicModeEnabled)
#endif #endif
} }
} }

View File

@ -247,7 +247,7 @@ struct SettingsView: View {
case .player: case .player:
return 450 return 450
case .controls: case .controls:
return 850 return 900
case .quality: case .quality:
return 420 return 420
case .history: case .history: