Files
yattee/Yattee/Models/PlayerControls/Gestures/TapGestureAction.swift
2026-02-08 18:33:56 +01:00

203 lines
6.7 KiB
Swift

//
// TapGestureAction.swift
// Yattee
//
// Defines the available actions for tap gestures.
//
import Foundation
/// Action to perform when a tap gesture zone is activated.
enum TapGestureAction: Codable, Hashable, Sendable {
/// Toggle play/pause state.
case togglePlayPause
/// Seek forward by the specified number of seconds.
case seekForward(seconds: Int)
/// Seek backward by the specified number of seconds.
case seekBackward(seconds: Int)
/// Toggle fullscreen mode.
case toggleFullscreen
/// Toggle Picture-in-Picture mode.
case togglePiP
/// Play next item in queue.
case playNext
/// Play previous item in queue.
case playPrevious
/// Cycle through playback speeds.
case cyclePlaybackSpeed
/// Toggle mute state.
case toggleMute
/// Display name for the action.
var displayName: String {
switch self {
case .togglePlayPause:
String(localized: "gestures.action.togglePlayPause", defaultValue: "Toggle Play/Pause")
case .seekForward(let seconds):
String(localized: "gestures.action.seekForward", defaultValue: "Seek Forward") + " \(seconds)s"
case .seekBackward(let seconds):
String(localized: "gestures.action.seekBackward", defaultValue: "Seek Backward") + " \(seconds)s"
case .toggleFullscreen:
String(localized: "gestures.action.toggleFullscreen", defaultValue: "Toggle Fullscreen")
case .togglePiP:
String(localized: "gestures.action.togglePiP", defaultValue: "Toggle PiP")
case .playNext:
String(localized: "gestures.action.playNext", defaultValue: "Play Next")
case .playPrevious:
String(localized: "gestures.action.playPrevious", defaultValue: "Play Previous")
case .cyclePlaybackSpeed:
String(localized: "gestures.action.cyclePlaybackSpeed", defaultValue: "Cycle Playback Speed")
case .toggleMute:
String(localized: "gestures.action.toggleMute", defaultValue: "Toggle Mute")
}
}
/// SF Symbol name for the action icon.
var systemImage: String {
switch self {
case .togglePlayPause:
"playpause.fill"
case .seekForward:
"arrow.trianglehead.clockwise"
case .seekBackward:
"arrow.trianglehead.counterclockwise"
case .toggleFullscreen:
"arrow.up.left.and.arrow.down.right"
case .togglePiP:
"pip"
case .playNext:
"forward.fill"
case .playPrevious:
"backward.fill"
case .cyclePlaybackSpeed:
"gauge.with.dots.needle.67percent"
case .toggleMute:
"speaker.slash.fill"
}
}
/// Base action type for grouping (ignoring associated values).
var actionType: TapGestureActionType {
switch self {
case .togglePlayPause: .togglePlayPause
case .seekForward: .seekForward
case .seekBackward: .seekBackward
case .toggleFullscreen: .toggleFullscreen
case .togglePiP: .togglePiP
case .playNext: .playNext
case .playPrevious: .playPrevious
case .cyclePlaybackSpeed: .cyclePlaybackSpeed
case .toggleMute: .toggleMute
}
}
/// Whether this action requires a seconds parameter.
var requiresSecondsParameter: Bool {
switch self {
case .seekForward, .seekBackward:
true
default:
false
}
}
/// The seek seconds value if applicable.
var seekSeconds: Int? {
switch self {
case .seekForward(let seconds), .seekBackward(let seconds):
seconds
default:
nil
}
}
}
// MARK: - Action Type Enum
/// Base action types without associated values (for UI selection).
enum TapGestureActionType: String, CaseIterable, Identifiable, Sendable {
case togglePlayPause
case seekForward
case seekBackward
case toggleFullscreen
case togglePiP
case playNext
case playPrevious
case cyclePlaybackSpeed
case toggleMute
var id: String { rawValue }
/// Display name for the action type.
var displayName: String {
switch self {
case .togglePlayPause:
String(localized: "gestures.actionType.togglePlayPause", defaultValue: "Toggle Play/Pause")
case .seekForward:
String(localized: "gestures.actionType.seekForward", defaultValue: "Seek Forward")
case .seekBackward:
String(localized: "gestures.actionType.seekBackward", defaultValue: "Seek Backward")
case .toggleFullscreen:
String(localized: "gestures.actionType.toggleFullscreen", defaultValue: "Toggle Fullscreen")
case .togglePiP:
String(localized: "gestures.actionType.togglePiP", defaultValue: "Toggle PiP")
case .playNext:
String(localized: "gestures.actionType.playNext", defaultValue: "Play Next")
case .playPrevious:
String(localized: "gestures.actionType.playPrevious", defaultValue: "Play Previous")
case .cyclePlaybackSpeed:
String(localized: "gestures.actionType.cyclePlaybackSpeed", defaultValue: "Cycle Playback Speed")
case .toggleMute:
String(localized: "gestures.actionType.toggleMute", defaultValue: "Toggle Mute")
}
}
/// SF Symbol name for the action type.
var systemImage: String {
switch self {
case .togglePlayPause: "playpause.fill"
case .seekForward: "arrow.trianglehead.clockwise"
case .seekBackward: "arrow.trianglehead.counterclockwise"
case .toggleFullscreen: "arrow.up.left.and.arrow.down.right"
case .togglePiP: "pip"
case .playNext: "forward.fill"
case .playPrevious: "backward.fill"
case .cyclePlaybackSpeed: "gauge.with.dots.needle.67percent"
case .toggleMute: "speaker.slash.fill"
}
}
/// Whether this action type requires a seconds parameter.
var requiresSecondsParameter: Bool {
switch self {
case .seekForward, .seekBackward:
true
default:
false
}
}
/// Creates a TapGestureAction from this type with default seconds if needed.
func toAction(seconds: Int = 10) -> TapGestureAction {
switch self {
case .togglePlayPause: .togglePlayPause
case .seekForward: .seekForward(seconds: seconds)
case .seekBackward: .seekBackward(seconds: seconds)
case .toggleFullscreen: .toggleFullscreen
case .togglePiP: .togglePiP
case .playNext: .playNext
case .playPrevious: .playPrevious
case .cyclePlaybackSpeed: .cyclePlaybackSpeed
case .toggleMute: .toggleMute
}
}
}