mirror of
https://github.com/yattee/yattee.git
synced 2026-07-20 14:22:02 +00:00
Playback menu seek items now follow the durations from the active player controls preset instead of hardcoded 10s/30s labels. AppEnvironment caches the active preset's center settings (refreshed on preset change notifications) so menu commands can read them synchronously. Add secondary seek durations (default 30s) to CenterSectionSettings, configurable on macOS in Seek Durations settings, used by Shift+arrows in the player and Cmd+Shift+arrows in the Playback menu. Also let Cmd-based key equivalents pass through the player keyboard monitor so menu shortcuts (e.g. Cmd+Option+arrow previous/next video) work while the player window has focus, and unify macOS built-in preset primary seek at 10s to match iOS/tvOS (built-in presets version 8).
133 lines
5.5 KiB
Swift
133 lines
5.5 KiB
Swift
//
|
|
// CenterSectionSettings.swift
|
|
// Yattee
|
|
//
|
|
// Settings for the center section of player controls (play/pause, seek buttons).
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Configuration for the center section of player controls.
|
|
/// Unlike top/bottom sections, center uses simple toggles rather than drag-and-drop.
|
|
struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|
/// Whether to show the play/pause button.
|
|
var showPlayPause: Bool
|
|
|
|
/// Whether to show the seek backward button.
|
|
var showSeekBackward: Bool
|
|
|
|
/// Whether to show the seek forward button.
|
|
var showSeekForward: Bool
|
|
|
|
/// Number of seconds for the seek backward button.
|
|
var seekBackwardSeconds: Int
|
|
|
|
/// Number of seconds for the seek forward button.
|
|
var seekForwardSeconds: Int
|
|
|
|
/// Number of seconds for the secondary seek backward shortcut (⇧ + seek).
|
|
var secondarySeekBackwardSeconds: Int
|
|
|
|
/// Number of seconds for the secondary seek forward shortcut (⇧ + seek).
|
|
var secondarySeekForwardSeconds: Int
|
|
|
|
/// Type of slider to show on the left edge of the player (iOS only).
|
|
var leftSlider: SideSliderType
|
|
|
|
/// Type of slider to show on the right edge of the player (iOS only).
|
|
var rightSlider: SideSliderType
|
|
|
|
// MARK: - Initialization
|
|
|
|
/// Creates center section settings.
|
|
/// - Parameters:
|
|
/// - showPlayPause: Show play/pause button. Defaults to true.
|
|
/// - showSeekBackward: Show seek backward button. Defaults to true.
|
|
/// - showSeekForward: Show seek forward button. Defaults to true.
|
|
/// - seekBackwardSeconds: Seconds to seek backward. Defaults to 10.
|
|
/// - seekForwardSeconds: Seconds to seek forward. Defaults to 10.
|
|
/// - secondarySeekBackwardSeconds: Seconds for the secondary (⇧) backward seek. Defaults to 30.
|
|
/// - secondarySeekForwardSeconds: Seconds for the secondary (⇧) forward seek. Defaults to 30.
|
|
/// - leftSlider: Type of slider on left edge. Defaults to disabled.
|
|
/// - rightSlider: Type of slider on right edge. Defaults to disabled.
|
|
init(
|
|
showPlayPause: Bool = true,
|
|
showSeekBackward: Bool = true,
|
|
showSeekForward: Bool = true,
|
|
seekBackwardSeconds: Int = 10,
|
|
seekForwardSeconds: Int = 10,
|
|
secondarySeekBackwardSeconds: Int = 30,
|
|
secondarySeekForwardSeconds: Int = 30,
|
|
leftSlider: SideSliderType = .disabled,
|
|
rightSlider: SideSliderType = .disabled
|
|
) {
|
|
self.showPlayPause = showPlayPause
|
|
self.showSeekBackward = showSeekBackward
|
|
self.showSeekForward = showSeekForward
|
|
self.seekBackwardSeconds = max(1, seekBackwardSeconds)
|
|
self.seekForwardSeconds = max(1, seekForwardSeconds)
|
|
self.secondarySeekBackwardSeconds = max(1, secondarySeekBackwardSeconds)
|
|
self.secondarySeekForwardSeconds = max(1, secondarySeekForwardSeconds)
|
|
self.leftSlider = leftSlider
|
|
self.rightSlider = rightSlider
|
|
}
|
|
|
|
// MARK: - Codable
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case showPlayPause
|
|
case showSeekBackward
|
|
case showSeekForward
|
|
case seekBackwardSeconds
|
|
case seekForwardSeconds
|
|
case secondarySeekBackwardSeconds
|
|
case secondarySeekForwardSeconds
|
|
case leftSlider
|
|
case rightSlider
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
showPlayPause = try container.decode(Bool.self, forKey: .showPlayPause)
|
|
showSeekBackward = try container.decode(Bool.self, forKey: .showSeekBackward)
|
|
showSeekForward = try container.decode(Bool.self, forKey: .showSeekForward)
|
|
seekBackwardSeconds = try container.decode(Int.self, forKey: .seekBackwardSeconds)
|
|
seekForwardSeconds = try container.decode(Int.self, forKey: .seekForwardSeconds)
|
|
// New properties with defaults for backward compatibility
|
|
secondarySeekBackwardSeconds = try container.decodeIfPresent(Int.self, forKey: .secondarySeekBackwardSeconds) ?? 30
|
|
secondarySeekForwardSeconds = try container.decodeIfPresent(Int.self, forKey: .secondarySeekForwardSeconds) ?? 30
|
|
leftSlider = try container.decodeIfPresent(SideSliderType.self, forKey: .leftSlider) ?? .disabled
|
|
rightSlider = try container.decodeIfPresent(SideSliderType.self, forKey: .rightSlider) ?? .disabled
|
|
}
|
|
|
|
// MARK: - Defaults
|
|
|
|
/// Default center section settings.
|
|
static let `default` = CenterSectionSettings()
|
|
|
|
// MARK: - SF Symbol Names
|
|
|
|
/// Seek seconds that have dedicated SF Symbol icons.
|
|
private static let validSeekIconValues = [5, 10, 15, 30, 45, 60, 75, 90]
|
|
|
|
/// SF Symbol name for the seek backward button based on configured seconds.
|
|
/// Uses numbered icon for standard values (5, 10, 15, 30, 45, 60, 75, 90),
|
|
/// plain arrow for other values.
|
|
var seekBackwardSystemImage: String {
|
|
if Self.validSeekIconValues.contains(seekBackwardSeconds) {
|
|
return "\(seekBackwardSeconds).arrow.trianglehead.counterclockwise"
|
|
}
|
|
return "arrow.trianglehead.counterclockwise"
|
|
}
|
|
|
|
/// SF Symbol name for the seek forward button based on configured seconds.
|
|
/// Uses numbered icon for standard values (5, 10, 15, 30, 45, 60, 75, 90),
|
|
/// plain arrow for other values.
|
|
var seekForwardSystemImage: String {
|
|
if Self.validSeekIconValues.contains(seekForwardSeconds) {
|
|
return "\(seekForwardSeconds).arrow.trianglehead.clockwise"
|
|
}
|
|
return "arrow.trianglehead.clockwise"
|
|
}
|
|
}
|