feat: add familiar single-key playback controls
This commit is contained in:
+36
-14
@@ -492,7 +492,7 @@ private struct PlayerContent: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(model.state.status == PLAYER_PLAYING ? "Pause" : "Play")
|
||||
.help(model.state.status == PLAYER_PLAYING ? "Pause (Space)" : "Play (Space)")
|
||||
.help(model.state.status == PLAYER_PLAYING ? "Pause (K / Space)" : "Play (K / Space)")
|
||||
.disabled(!model.canTogglePlayback)
|
||||
iconButton("forward.fill", "Seek Forward 5 Seconds") { model.seekRelative(5) }
|
||||
.disabled(!model.canSeek)
|
||||
@@ -685,6 +685,35 @@ private struct WindowChrome: NSViewRepresentable {
|
||||
}
|
||||
required init?(coder: NSCoder) { nil }
|
||||
|
||||
func handleKeyEvent(_ event: NSEvent) -> Bool {
|
||||
guard event.type == .keyDown, let window = configuredWindow, event.window === window,
|
||||
!(window.firstResponder is NSTextView),
|
||||
event.modifierFlags.intersection([.command, .control, .option]).isEmpty else { return false }
|
||||
if event.keyCode == 53 && model.isFullScreen {
|
||||
if !event.isARepeat { model.toggleFullScreen() }
|
||||
return true
|
||||
}
|
||||
let key = event.charactersIgnoringModifiers?.lowercased()
|
||||
switch key {
|
||||
case "j": model.seekRelative(-10)
|
||||
case "l": model.seekRelative(10)
|
||||
case "k", " ":
|
||||
if !event.isARepeat { model.togglePlayback() }
|
||||
case "m":
|
||||
if !event.isARepeat { model.toggleMute() }
|
||||
case "f":
|
||||
if !event.isARepeat { model.toggleFullScreen() }
|
||||
case "p":
|
||||
if !event.isARepeat { model.toggleOnTop() }
|
||||
case "o":
|
||||
if !event.isARepeat { model.openPanel() }
|
||||
case "q":
|
||||
if !event.isARepeat { NSApp.terminate(nil) }
|
||||
default: return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override func viewDidMoveToWindow() {
|
||||
super.viewDidMoveToWindow()
|
||||
guard let window else { detach(); return }
|
||||
@@ -713,17 +742,8 @@ private struct WindowChrome: NSViewRepresentable {
|
||||
if event.type == .keyDown && event.keyCode == 48 { self.model.revealControls() }
|
||||
if event.type == .mouseMoved || event.type == .leftMouseDown {
|
||||
self.model.revealControls()
|
||||
} else if event.type == .keyDown && !(event.window?.firstResponder is NSSlider) &&
|
||||
!(event.window?.firstResponder is NSTextView) {
|
||||
if event.keyCode == 53 && self.model.isFullScreen {
|
||||
self.model.toggleFullScreen()
|
||||
return true
|
||||
}
|
||||
if event.modifierFlags.intersection([.command, .control, .option]).isEmpty &&
|
||||
event.charactersIgnoringModifiers?.lowercased() == "f" {
|
||||
self.model.toggleFullScreen()
|
||||
return true
|
||||
}
|
||||
} else if event.type == .keyDown {
|
||||
return self.handleKeyEvent(event)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -797,18 +817,20 @@ struct PlayerApp: App {
|
||||
}
|
||||
CommandMenu("Playback") {
|
||||
Button(model.state.status == PLAYER_PLAYING ? "Pause" : "Play", action: model.togglePlayback)
|
||||
.keyboardShortcut(.space, modifiers: []).disabled(!model.canTogglePlayback)
|
||||
.disabled(!model.canTogglePlayback)
|
||||
Button("Seek Back 5 Seconds") { model.seekRelative(-5) }
|
||||
.keyboardShortcut(.leftArrow, modifiers: []).disabled(!model.canSeek)
|
||||
Button("Seek Forward 5 Seconds") { model.seekRelative(5) }
|
||||
.keyboardShortcut(.rightArrow, modifiers: []).disabled(!model.canSeek)
|
||||
Button("Seek Back 10 Seconds") { model.seekRelative(-10) }.disabled(!model.canSeek)
|
||||
Button("Seek Forward 10 Seconds") { model.seekRelative(10) }.disabled(!model.canSeek)
|
||||
Divider()
|
||||
Button("Volume Up") { model.setVolume(min(1, model.volume + 0.05)) }
|
||||
.keyboardShortcut(.upArrow, modifiers: []).disabled(!model.canSetVolume)
|
||||
Button("Volume Down") { model.setVolume(max(0, model.volume - 0.05)) }
|
||||
.keyboardShortcut(.downArrow, modifiers: []).disabled(!model.canSetVolume)
|
||||
Button(model.volume == 0 ? "Unmute" : "Mute", action: model.toggleMute)
|
||||
.keyboardShortcut("m", modifiers: []).disabled(!model.canSetVolume)
|
||||
.disabled(!model.canSetVolume)
|
||||
}
|
||||
CommandGroup(after: .windowArrangement) {
|
||||
Button(model.isFullScreen ? "Exit Full Screen" : "Enter Full Screen", action: model.toggleFullScreen)
|
||||
|
||||
Reference in New Issue
Block a user