mirror of
https://github.com/yattee/yattee.git
synced 2026-07-20 14:22:02 +00:00
Make seek durations configurable in Playback menu and add secondary seek
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).
This commit is contained in:
@@ -56,6 +56,13 @@ final class AppEnvironment {
|
|||||||
let legacyMigrationService: LegacyDataMigrationService
|
let legacyMigrationService: LegacyDataMigrationService
|
||||||
let sourcesSettings: SourcesSettings
|
let sourcesSettings: SourcesSettings
|
||||||
|
|
||||||
|
/// Center-section settings of the active player controls preset, cached for
|
||||||
|
/// synchronous access. Menu bar commands read seek durations from here since
|
||||||
|
/// they cannot await the layout service actor.
|
||||||
|
private(set) var activeControlsCenterSettings: CenterSectionSettings = .default
|
||||||
|
|
||||||
|
@ObservationIgnored private var controlsSettingsObservers: [NSObjectProtocol] = []
|
||||||
|
|
||||||
// MARK: - Shared Instance
|
// MARK: - Shared Instance
|
||||||
|
|
||||||
/// The single, process-wide app environment.
|
/// The single, process-wide app environment.
|
||||||
@@ -311,6 +318,18 @@ final class AppEnvironment {
|
|||||||
// Wire up player controls layout service to player service (for preset-based settings)
|
// Wire up player controls layout service to player service (for preset-based settings)
|
||||||
player.setPlayerControlsLayoutService(layoutService)
|
player.setPlayerControlsLayoutService(layoutService)
|
||||||
|
|
||||||
|
// Cache active preset's center settings and keep them in sync
|
||||||
|
Task { await self.refreshActiveControlsSettings() }
|
||||||
|
for name: Notification.Name in [.playerControlsActivePresetDidChange, .playerControlsPresetsDidChange] {
|
||||||
|
controlsSettingsObservers.append(
|
||||||
|
NotificationCenter.default.addObserver(forName: name, object: nil, queue: .main) { [weak self] _ in
|
||||||
|
Task { @MainActor in
|
||||||
|
await self?.refreshActiveControlsSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Set up circular dependencies after all properties are initialized
|
// Set up circular dependencies after all properties are initialized
|
||||||
bgRefreshManager.setAppEnvironment(self)
|
bgRefreshManager.setAppEnvironment(self)
|
||||||
|
|
||||||
@@ -350,6 +369,12 @@ final class AppEnvironment {
|
|||||||
|
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
|
|
||||||
|
/// Refreshes the cached center-section settings from the active player controls preset.
|
||||||
|
func refreshActiveControlsSettings() async {
|
||||||
|
let layout = await playerControlsLayoutService.activeLayout()
|
||||||
|
activeControlsCenterSettings = layout.centerSettings
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates the HTTP client's User-Agent configuration from current settings.
|
/// Updates the HTTP client's User-Agent configuration from current settings.
|
||||||
/// Call this after changing User-Agent related settings.
|
/// Call this after changing User-Agent related settings.
|
||||||
func updateUserAgent() {
|
func updateUserAgent() {
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ struct PlaybackCommands: Commands {
|
|||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
// Seeking
|
// Seeking
|
||||||
seekBackward10Button
|
seekBackwardButton
|
||||||
seekForward10Button
|
seekForwardButton
|
||||||
seekBackward30Button
|
secondarySeekBackwardButton
|
||||||
seekForward30Button
|
secondarySeekForwardButton
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
@@ -121,41 +121,59 @@ struct PlaybackCommands: Commands {
|
|||||||
|
|
||||||
// MARK: - Seeking
|
// MARK: - Seeking
|
||||||
|
|
||||||
private var seekBackward10Button: some View {
|
/// Seek durations follow the active player controls preset, matching the
|
||||||
|
/// in-player arrow key shortcuts.
|
||||||
|
private var seekBackwardSeconds: Int {
|
||||||
|
appEnvironment.activeControlsCenterSettings.seekBackwardSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
private var seekForwardSeconds: Int {
|
||||||
|
appEnvironment.activeControlsCenterSettings.seekForwardSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
private var seekBackwardButton: some View {
|
||||||
Button {
|
Button {
|
||||||
playerService.seekBackward(by: 10)
|
playerService.seekBackward(by: TimeInterval(seekBackwardSeconds))
|
||||||
} label: {
|
} label: {
|
||||||
Text(String(localized: "menu.playback.seekBackward10"))
|
Text(String(localized: "menu.playback.seekBackward \(seekBackwardSeconds)"))
|
||||||
}
|
}
|
||||||
.keyboardShortcut(.leftArrow, modifiers: [.command])
|
.keyboardShortcut(.leftArrow, modifiers: [.command])
|
||||||
.disabled(!hasActiveVideo)
|
.disabled(!hasActiveVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var seekForward10Button: some View {
|
private var seekForwardButton: some View {
|
||||||
Button {
|
Button {
|
||||||
playerService.seekForward(by: 10)
|
playerService.seekForward(by: TimeInterval(seekForwardSeconds))
|
||||||
} label: {
|
} label: {
|
||||||
Text(String(localized: "menu.playback.seekForward10"))
|
Text(String(localized: "menu.playback.seekForward \(seekForwardSeconds)"))
|
||||||
}
|
}
|
||||||
.keyboardShortcut(.rightArrow, modifiers: [.command])
|
.keyboardShortcut(.rightArrow, modifiers: [.command])
|
||||||
.disabled(!hasActiveVideo)
|
.disabled(!hasActiveVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var seekBackward30Button: some View {
|
private var secondarySeekBackwardSeconds: Int {
|
||||||
|
appEnvironment.activeControlsCenterSettings.secondarySeekBackwardSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
private var secondarySeekForwardSeconds: Int {
|
||||||
|
appEnvironment.activeControlsCenterSettings.secondarySeekForwardSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
private var secondarySeekBackwardButton: some View {
|
||||||
Button {
|
Button {
|
||||||
playerService.seekBackward(by: 30)
|
playerService.seekBackward(by: TimeInterval(secondarySeekBackwardSeconds))
|
||||||
} label: {
|
} label: {
|
||||||
Text(String(localized: "menu.playback.seekBackward30"))
|
Text(String(localized: "menu.playback.seekBackward \(secondarySeekBackwardSeconds)"))
|
||||||
}
|
}
|
||||||
.keyboardShortcut(.leftArrow, modifiers: [.command, .shift])
|
.keyboardShortcut(.leftArrow, modifiers: [.command, .shift])
|
||||||
.disabled(!hasActiveVideo)
|
.disabled(!hasActiveVideo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var seekForward30Button: some View {
|
private var secondarySeekForwardButton: some View {
|
||||||
Button {
|
Button {
|
||||||
playerService.seekForward(by: 30)
|
playerService.seekForward(by: TimeInterval(secondarySeekForwardSeconds))
|
||||||
} label: {
|
} label: {
|
||||||
Text(String(localized: "menu.playback.seekForward30"))
|
Text(String(localized: "menu.playback.seekForward \(secondarySeekForwardSeconds)"))
|
||||||
}
|
}
|
||||||
.keyboardShortcut(.rightArrow, modifiers: [.command, .shift])
|
.keyboardShortcut(.rightArrow, modifiers: [.command, .shift])
|
||||||
.disabled(!hasActiveVideo)
|
.disabled(!hasActiveVideo)
|
||||||
|
|||||||
@@ -5741,42 +5741,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"menu.playback.seekBackward10" : {
|
"menu.playback.seekBackward %lld" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "translated",
|
||||||
"value" : "Seek Back 10s"
|
"value" : "Seek Back %llds"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"menu.playback.seekBackward30" : {
|
"menu.playback.seekForward %lld" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "translated",
|
||||||
"value" : "Seek Back 30s"
|
"value" : "Seek Forward %llds"
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"menu.playback.seekForward10" : {
|
|
||||||
"localizations" : {
|
|
||||||
"en" : {
|
|
||||||
"stringUnit" : {
|
|
||||||
"state" : "translated",
|
|
||||||
"value" : "Seek Forward 10s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"menu.playback.seekForward30" : {
|
|
||||||
"localizations" : {
|
|
||||||
"en" : {
|
|
||||||
"stringUnit" : {
|
|
||||||
"state" : "translated",
|
|
||||||
"value" : "Seek Forward 30s"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13260,6 +13240,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"settings.playerControls.center.secondarySeekBackwardTime" : {
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Secondary Seek Time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings.playerControls.center.secondarySeekForwardTime" : {
|
||||||
|
"extractionState" : "extracted_with_value",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "new",
|
||||||
|
"value" : "Secondary Seek Time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"settings.playerControls.center.seekBackward" : {
|
"settings.playerControls.center.seekBackward" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@@ -13286,7 +13288,7 @@
|
|||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "new",
|
"state" : "new",
|
||||||
"value" : "Seek durations are used by the ← and → keyboard shortcuts and by seek buttons added to the player."
|
"value" : "Seek durations are used by the ← and → keyboard shortcuts and by seek buttons added to the player. Hold ⇧ Shift with ← or → to seek by the secondary durations."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ extension LayoutPreset {
|
|||||||
/// Bump this version whenever any built-in preset definition changes.
|
/// Bump this version whenever any built-in preset definition changes.
|
||||||
/// On launch, the app compares this against the last-applied version
|
/// On launch, the app compares this against the last-applied version
|
||||||
/// and replaces stale built-in presets with fresh copies from code.
|
/// and replaces stale built-in presets with fresh copies from code.
|
||||||
static let builtInPresetsVersion = 7
|
static let builtInPresetsVersion = 8
|
||||||
|
|
||||||
// MARK: - Built-in Preset IDs
|
// MARK: - Built-in Preset IDs
|
||||||
|
|
||||||
@@ -164,13 +164,13 @@ extension LayoutPreset {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// On macOS center settings only drive seek amounts (keyboard arrows
|
// On macOS center settings only drive seek amounts (keyboard arrows
|
||||||
// and default seek buttons); 5s preserves the historical arrow-key step.
|
// and default seek buttons).
|
||||||
let centerSettings = CenterSectionSettings(
|
let centerSettings = CenterSectionSettings(
|
||||||
showPlayPause: true,
|
showPlayPause: true,
|
||||||
showSeekBackward: true,
|
showSeekBackward: true,
|
||||||
showSeekForward: true,
|
showSeekForward: true,
|
||||||
seekBackwardSeconds: 5,
|
seekBackwardSeconds: 10,
|
||||||
seekForwardSeconds: 5,
|
seekForwardSeconds: 10,
|
||||||
leftSlider: .disabled,
|
leftSlider: .disabled,
|
||||||
rightSlider: .disabled
|
rightSlider: .disabled
|
||||||
)
|
)
|
||||||
@@ -308,8 +308,8 @@ extension LayoutPreset {
|
|||||||
showPlayPause: true,
|
showPlayPause: true,
|
||||||
showSeekBackward: true,
|
showSeekBackward: true,
|
||||||
showSeekForward: true,
|
showSeekForward: true,
|
||||||
seekBackwardSeconds: 5,
|
seekBackwardSeconds: 10,
|
||||||
seekForwardSeconds: 5,
|
seekForwardSeconds: 10,
|
||||||
leftSlider: .disabled,
|
leftSlider: .disabled,
|
||||||
rightSlider: .disabled
|
rightSlider: .disabled
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|||||||
/// Number of seconds for the seek forward button.
|
/// Number of seconds for the seek forward button.
|
||||||
var seekForwardSeconds: Int
|
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).
|
/// Type of slider to show on the left edge of the player (iOS only).
|
||||||
var leftSlider: SideSliderType
|
var leftSlider: SideSliderType
|
||||||
|
|
||||||
@@ -40,6 +46,8 @@ struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|||||||
/// - showSeekForward: Show seek forward button. Defaults to true.
|
/// - showSeekForward: Show seek forward button. Defaults to true.
|
||||||
/// - seekBackwardSeconds: Seconds to seek backward. Defaults to 10.
|
/// - seekBackwardSeconds: Seconds to seek backward. Defaults to 10.
|
||||||
/// - seekForwardSeconds: Seconds to seek forward. 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.
|
/// - leftSlider: Type of slider on left edge. Defaults to disabled.
|
||||||
/// - rightSlider: Type of slider on right edge. Defaults to disabled.
|
/// - rightSlider: Type of slider on right edge. Defaults to disabled.
|
||||||
init(
|
init(
|
||||||
@@ -48,6 +56,8 @@ struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|||||||
showSeekForward: Bool = true,
|
showSeekForward: Bool = true,
|
||||||
seekBackwardSeconds: Int = 10,
|
seekBackwardSeconds: Int = 10,
|
||||||
seekForwardSeconds: Int = 10,
|
seekForwardSeconds: Int = 10,
|
||||||
|
secondarySeekBackwardSeconds: Int = 30,
|
||||||
|
secondarySeekForwardSeconds: Int = 30,
|
||||||
leftSlider: SideSliderType = .disabled,
|
leftSlider: SideSliderType = .disabled,
|
||||||
rightSlider: SideSliderType = .disabled
|
rightSlider: SideSliderType = .disabled
|
||||||
) {
|
) {
|
||||||
@@ -56,6 +66,8 @@ struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|||||||
self.showSeekForward = showSeekForward
|
self.showSeekForward = showSeekForward
|
||||||
self.seekBackwardSeconds = max(1, seekBackwardSeconds)
|
self.seekBackwardSeconds = max(1, seekBackwardSeconds)
|
||||||
self.seekForwardSeconds = max(1, seekForwardSeconds)
|
self.seekForwardSeconds = max(1, seekForwardSeconds)
|
||||||
|
self.secondarySeekBackwardSeconds = max(1, secondarySeekBackwardSeconds)
|
||||||
|
self.secondarySeekForwardSeconds = max(1, secondarySeekForwardSeconds)
|
||||||
self.leftSlider = leftSlider
|
self.leftSlider = leftSlider
|
||||||
self.rightSlider = rightSlider
|
self.rightSlider = rightSlider
|
||||||
}
|
}
|
||||||
@@ -68,6 +80,8 @@ struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|||||||
case showSeekForward
|
case showSeekForward
|
||||||
case seekBackwardSeconds
|
case seekBackwardSeconds
|
||||||
case seekForwardSeconds
|
case seekForwardSeconds
|
||||||
|
case secondarySeekBackwardSeconds
|
||||||
|
case secondarySeekForwardSeconds
|
||||||
case leftSlider
|
case leftSlider
|
||||||
case rightSlider
|
case rightSlider
|
||||||
}
|
}
|
||||||
@@ -80,6 +94,8 @@ struct CenterSectionSettings: Codable, Hashable, Sendable {
|
|||||||
seekBackwardSeconds = try container.decode(Int.self, forKey: .seekBackwardSeconds)
|
seekBackwardSeconds = try container.decode(Int.self, forKey: .seekBackwardSeconds)
|
||||||
seekForwardSeconds = try container.decode(Int.self, forKey: .seekForwardSeconds)
|
seekForwardSeconds = try container.decode(Int.self, forKey: .seekForwardSeconds)
|
||||||
// New properties with defaults for backward compatibility
|
// 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
|
leftSlider = try container.decodeIfPresent(SideSliderType.self, forKey: .leftSlider) ?? .disabled
|
||||||
rightSlider = try container.decodeIfPresent(SideSliderType.self, forKey: .rightSlider) ?? .disabled
|
rightSlider = try container.decodeIfPresent(SideSliderType.self, forKey: .rightSlider) ?? .disabled
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,6 +325,12 @@ struct MacOSPlayerControlsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let ⌘-based key equivalents reach the menu bar (Playback menu shortcuts
|
||||||
|
// like ⌘←/⌘⇧← seek and ⌘⌥← previous video) instead of consuming them here.
|
||||||
|
if event.modifierFlags.contains(.command) { return event }
|
||||||
|
|
||||||
|
let isShiftHeld = event.modifierFlags.contains(.shift)
|
||||||
|
|
||||||
// Handle keyboard shortcuts
|
// Handle keyboard shortcuts
|
||||||
switch event.keyCode {
|
switch event.keyCode {
|
||||||
case 49: // Space
|
case 49: // Space
|
||||||
@@ -332,12 +338,20 @@ struct MacOSPlayerControlsView: View {
|
|||||||
return nil // Consume event
|
return nil // Consume event
|
||||||
|
|
||||||
case 123: // Left arrow
|
case 123: // Left arrow
|
||||||
let seconds = TimeInterval(layout?.centerSettings.seekBackwardSeconds ?? 5)
|
let seconds = TimeInterval(
|
||||||
|
isShiftHeld
|
||||||
|
? layout?.centerSettings.secondarySeekBackwardSeconds ?? 30
|
||||||
|
: layout?.centerSettings.seekBackwardSeconds ?? 10
|
||||||
|
)
|
||||||
Task { await onSeekBackward(seconds) }
|
Task { await onSeekBackward(seconds) }
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case 124: // Right arrow
|
case 124: // Right arrow
|
||||||
let seconds = TimeInterval(layout?.centerSettings.seekForwardSeconds ?? 5)
|
let seconds = TimeInterval(
|
||||||
|
isShiftHeld
|
||||||
|
? layout?.centerSettings.secondarySeekForwardSeconds ?? 30
|
||||||
|
: layout?.centerSettings.seekForwardSeconds ?? 10
|
||||||
|
)
|
||||||
Task { await onSeekForward(seconds) }
|
Task { await onSeekForward(seconds) }
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ struct CenterControlsSettingsView: View {
|
|||||||
@State private var showSeekForward: Bool = true
|
@State private var showSeekForward: Bool = true
|
||||||
@State private var seekBackwardSeconds: Double = 10
|
@State private var seekBackwardSeconds: Double = 10
|
||||||
@State private var seekForwardSeconds: Double = 10
|
@State private var seekForwardSeconds: Double = 10
|
||||||
|
@State private var secondarySeekBackwardSeconds: Double = 30
|
||||||
|
@State private var secondarySeekForwardSeconds: Double = 30
|
||||||
@State private var leftSlider: SideSliderType = .disabled
|
@State private var leftSlider: SideSliderType = .disabled
|
||||||
@State private var rightSlider: SideSliderType = .disabled
|
@State private var rightSlider: SideSliderType = .disabled
|
||||||
|
|
||||||
@@ -113,6 +115,39 @@ struct CenterControlsSettingsView: View {
|
|||||||
.tint(Int(seekBackwardSeconds) == seconds ? .accentColor : .secondary)
|
.tint(Int(seekBackwardSeconds) == seconds ? .accentColor : .secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
HStack {
|
||||||
|
Text(String(
|
||||||
|
localized: "settings.playerControls.center.secondarySeekBackwardTime",
|
||||||
|
defaultValue: "Secondary Seek Time"
|
||||||
|
))
|
||||||
|
Spacer()
|
||||||
|
Text("\(Int(secondarySeekBackwardSeconds))s")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider(
|
||||||
|
value: $secondarySeekBackwardSeconds,
|
||||||
|
in: 1...90,
|
||||||
|
step: 1
|
||||||
|
)
|
||||||
|
.onChange(of: secondarySeekBackwardSeconds) { _, newValue in
|
||||||
|
viewModel.updateCenterSettingsSync { $0.secondarySeekBackwardSeconds = Int(newValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick presets
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
ForEach([15, 30, 45, 60, 90], id: \.self) { seconds in
|
||||||
|
Button("\(seconds)s") {
|
||||||
|
secondarySeekBackwardSeconds = Double(seconds)
|
||||||
|
}
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
.controlSize(.small)
|
||||||
|
.tint(Int(secondarySeekBackwardSeconds) == seconds ? .accentColor : .secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text(String(localized: "settings.playerControls.center.seekBackward"))
|
Text(String(localized: "settings.playerControls.center.seekBackward"))
|
||||||
@@ -162,6 +197,39 @@ struct CenterControlsSettingsView: View {
|
|||||||
.tint(Int(seekForwardSeconds) == seconds ? .accentColor : .secondary)
|
.tint(Int(seekForwardSeconds) == seconds ? .accentColor : .secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
HStack {
|
||||||
|
Text(String(
|
||||||
|
localized: "settings.playerControls.center.secondarySeekForwardTime",
|
||||||
|
defaultValue: "Secondary Seek Time"
|
||||||
|
))
|
||||||
|
Spacer()
|
||||||
|
Text("\(Int(secondarySeekForwardSeconds))s")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider(
|
||||||
|
value: $secondarySeekForwardSeconds,
|
||||||
|
in: 1...90,
|
||||||
|
step: 1
|
||||||
|
)
|
||||||
|
.onChange(of: secondarySeekForwardSeconds) { _, newValue in
|
||||||
|
viewModel.updateCenterSettingsSync { $0.secondarySeekForwardSeconds = Int(newValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick presets
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
ForEach([15, 30, 45, 60, 90], id: \.self) { seconds in
|
||||||
|
Button("\(seconds)s") {
|
||||||
|
secondarySeekForwardSeconds = Double(seconds)
|
||||||
|
}
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
.controlSize(.small)
|
||||||
|
.tint(Int(secondarySeekForwardSeconds) == seconds ? .accentColor : .secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text(String(localized: "settings.playerControls.center.seekForward"))
|
Text(String(localized: "settings.playerControls.center.seekForward"))
|
||||||
@@ -169,7 +237,7 @@ struct CenterControlsSettingsView: View {
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
Text(String(
|
Text(String(
|
||||||
localized: "settings.playerControls.center.seekDurationsFooter",
|
localized: "settings.playerControls.center.seekDurationsFooter",
|
||||||
defaultValue: "Seek durations are used by the ← and → keyboard shortcuts and by seek buttons added to the player."
|
defaultValue: "Seek durations are used by the ← and → keyboard shortcuts and by seek buttons added to the player. Hold ⇧ Shift with ← or → to seek by the secondary durations."
|
||||||
))
|
))
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -231,6 +299,8 @@ struct CenterControlsSettingsView: View {
|
|||||||
showSeekForward: showSeekForward,
|
showSeekForward: showSeekForward,
|
||||||
seekBackwardSeconds: Int(seekBackwardSeconds),
|
seekBackwardSeconds: Int(seekBackwardSeconds),
|
||||||
seekForwardSeconds: Int(seekForwardSeconds),
|
seekForwardSeconds: Int(seekForwardSeconds),
|
||||||
|
secondarySeekBackwardSeconds: Int(secondarySeekBackwardSeconds),
|
||||||
|
secondarySeekForwardSeconds: Int(secondarySeekForwardSeconds),
|
||||||
leftSlider: leftSlider,
|
leftSlider: leftSlider,
|
||||||
rightSlider: rightSlider
|
rightSlider: rightSlider
|
||||||
)
|
)
|
||||||
@@ -246,6 +316,8 @@ struct CenterControlsSettingsView: View {
|
|||||||
showSeekForward = settings.showSeekForward
|
showSeekForward = settings.showSeekForward
|
||||||
seekBackwardSeconds = Double(settings.seekBackwardSeconds)
|
seekBackwardSeconds = Double(settings.seekBackwardSeconds)
|
||||||
seekForwardSeconds = Double(settings.seekForwardSeconds)
|
seekForwardSeconds = Double(settings.seekForwardSeconds)
|
||||||
|
secondarySeekBackwardSeconds = Double(settings.secondarySeekBackwardSeconds)
|
||||||
|
secondarySeekForwardSeconds = Double(settings.secondarySeekForwardSeconds)
|
||||||
leftSlider = settings.leftSlider
|
leftSlider = settings.leftSlider
|
||||||
rightSlider = settings.rightSlider
|
rightSlider = settings.rightSlider
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user