feat: configure explicit state-dependent player keybindings
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
public enum PlayerAction: Hashable, Sendable {
|
||||
case play, pause, mute, unmute, volumeUp, volumeDown
|
||||
case seekForward5s, seekBackward5s, seekForward10s, seekBackward10s
|
||||
case stepFrameForward, stepFrameBackward
|
||||
case enterFullscreen, exitFullscreen, enterCompact, exitCompact
|
||||
case enableAlwaysOnTop, disableAlwaysOnTop
|
||||
case openFile, openYoutube, closeFile, quit
|
||||
case showBufferInfo, hideBufferInfo
|
||||
case enableCompactSnapping, disableCompactSnapping
|
||||
case enableAutomaticQuality, disableAutomaticQuality
|
||||
case qualityDefault, quality(Int)
|
||||
|
||||
static let namedActions: [(String, PlayerAction)] = [
|
||||
("play", .play), ("pause", .pause), ("mute", .mute), ("unmute", .unmute),
|
||||
("volume_up", .volumeUp), ("volume_down", .volumeDown),
|
||||
("seek_forward_5s", .seekForward5s), ("seek_backward_5s", .seekBackward5s),
|
||||
("seek_forward_10s", .seekForward10s), ("seek_backward_10s", .seekBackward10s),
|
||||
("step_frame_forward", .stepFrameForward), ("step_frame_backward", .stepFrameBackward),
|
||||
("enter_fullscreen", .enterFullscreen), ("exit_fullscreen", .exitFullscreen),
|
||||
("enter_compact", .enterCompact), ("exit_compact", .exitCompact),
|
||||
("enable_always_on_top", .enableAlwaysOnTop), ("disable_always_on_top", .disableAlwaysOnTop),
|
||||
("open_file", .openFile), ("open_youtube", .openYoutube), ("close_file", .closeFile), ("quit", .quit),
|
||||
("show_buffer_info", .showBufferInfo), ("hide_buffer_info", .hideBufferInfo),
|
||||
("enable_compact_snapping", .enableCompactSnapping), ("disable_compact_snapping", .disableCompactSnapping),
|
||||
("enable_automatic_quality", .enableAutomaticQuality), ("disable_automatic_quality", .disableAutomaticQuality),
|
||||
("quality_default", .qualityDefault)
|
||||
]
|
||||
private static let actionsByName = Dictionary(uniqueKeysWithValues: namedActions)
|
||||
private static let namesByAction = Dictionary(uniqueKeysWithValues: namedActions.map { ($0.1, $0.0) })
|
||||
|
||||
public init?(name: String) {
|
||||
if let action = Self.actionsByName[name] {
|
||||
self = action
|
||||
} else if name.hasPrefix("quality_"), name.hasSuffix("p") {
|
||||
let digits = name.dropFirst("quality_".count).dropLast()
|
||||
guard !digits.isEmpty, digits.allSatisfy({ $0 >= "0" && $0 <= "9" }),
|
||||
let height = Int(digits), height > 0, String(height) == digits else { return nil }
|
||||
self = .quality(height)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var name: String {
|
||||
if case .quality(let height) = self { return "quality_\(height)p" }
|
||||
return Self.namesByAction[self]!
|
||||
}
|
||||
|
||||
public var inverse: PlayerAction? {
|
||||
switch self {
|
||||
case .play: return .pause
|
||||
case .pause: return .play
|
||||
case .mute: return .unmute
|
||||
case .unmute: return .mute
|
||||
case .enterFullscreen: return .exitFullscreen
|
||||
case .exitFullscreen: return .enterFullscreen
|
||||
case .enterCompact: return .exitCompact
|
||||
case .exitCompact: return .enterCompact
|
||||
case .enableAlwaysOnTop: return .disableAlwaysOnTop
|
||||
case .disableAlwaysOnTop: return .enableAlwaysOnTop
|
||||
case .showBufferInfo: return .hideBufferInfo
|
||||
case .hideBufferInfo: return .showBufferInfo
|
||||
case .enableCompactSnapping: return .disableCompactSnapping
|
||||
case .disableCompactSnapping: return .enableCompactSnapping
|
||||
case .enableAutomaticQuality: return .disableAutomaticQuality
|
||||
case .disableAutomaticQuality: return .enableAutomaticQuality
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var allowsRepeat: Bool {
|
||||
switch self {
|
||||
case .volumeUp, .volumeDown, .seekForward5s, .seekBackward5s,
|
||||
.seekForward10s, .seekBackward10s, .stepFrameForward, .stepFrameBackward: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user