Skip to content

Commit

Permalink
Fix key bindings for seek exact seeks by keyframes, iina#3710 (iina#4739
Browse files Browse the repository at this point in the history
)

This commit will:
- Change MenuController.sameKeyAction to detect the exact flag and pass
  it on as extra data
- Change MainMenuActionHandler.menuStep to pass the extra data to
  PlayerCore.seek if present

The main author of these changes is Matt Svoboda. I merely addressed
some review comments.

Co-authored-by: Matt Svoboda <[email protected]>
  • Loading branch information
low-batt and svobs authored Jan 2, 2024
1 parent dc56695 commit e343c28
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 20 deletions.
14 changes: 9 additions & 5 deletions iina/MainMenuActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,15 @@ extension MainMenuActionHandler {
}

@objc func menuStep(_ sender: NSMenuItem) {
let seconds = Double(abs((sender.representedObject as? Int) ?? 5))
if sender.tag == 0 { // -> 5s
player.seek(relativeSecond: seconds, option: .relative)
} else if sender.tag == 1 { // <- 5s
player.seek(relativeSecond: -seconds, option: .relative)
if let args = sender.representedObject as? (Double, Preference.SeekOption) {
player.seek(relativeSecond: args.0, option: args.1)
} else {
let seconds = Double(abs((sender.representedObject as? Int) ?? 5))
if sender.tag == 0 { // -> 5s
player.seek(relativeSecond: seconds, option: Preference.SeekOption.defaultValue)
} else if sender.tag == 1 { // <- 5s
player.seek(relativeSecond: -seconds, option: Preference.SeekOption.defaultValue)
}
}
}

Expand Down
67 changes: 52 additions & 15 deletions iina/MenuController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,65 @@

import Cocoa

fileprivate func sameKeyAction(_ lhs: [String], _ rhs: [String], _ normalizeLastNum: Bool, _ numRange: ClosedRange<Double>?) -> (Bool, Double?) {
fileprivate func sameKeyAction(_ lhs: [String], _ rhs: [String], _ normalizeLastNum: Bool, _ numRange: ClosedRange<Double>?) -> (Bool, Double?, Any?) {
var lhs = lhs
if lhs.first == "seek" && (lhs.last == "exact" || lhs.last == "keyframe") {
lhs = [String](lhs.dropLast())
var extraData: Any? = nil
if lhs.first == "seek", rhs.first == "seek", lhs.count > 2, let last = lhs.last {
// This is a seek command that includes flags. Adjust the command before checking for a match.
if lhs.count == 4 {
// The original mpv seek command required that the keyframes and exact flags be passed as a
// 3rd parameter. This is considered deprecated but still supported by mpv. Convert this to
// the current command format by combining the flags using a "+" separator.
lhs[2] = "\(lhs[2])+\(lhs[3])"
lhs = [String](lhs.dropLast())
}
var splitArray = last.split(whereSeparator: { $0 == "+" })
if let index = splitArray.firstIndex(of: "relative") {
// The mpv seek command seeks relative to current position by default. Because of that the
// seek command used by menu items does not specify this flag. Ignore it when checking for a
// match.
splitArray.remove(at: index)
}
if let index = splitArray.firstIndex(of: "exact") {
// Alter the behavior of the menu item by passing this flag on the side as extra data.
splitArray.remove(at: index)
extraData = Preference.SeekOption.exact
}
// NOTE at this time PlayerCore does not support specifying the keyframes flag, so it can't
// be specified on the side as extra data as is done for exact. Although the mpv seek command
// normally defaults to seeking by keyframes, that default can be changed by the hr-seek option.
// When hr-seek has been set to enable exact seeks by default the keyframes flag will override
// that default.
if splitArray.isEmpty {
// All flags were recognized as ones we do not need to consider when checking for a match.
lhs = [String](lhs.dropLast())
}
}
guard lhs.count > 0 && lhs.count == rhs.count else {
return (false, nil)
return (false, nil, nil)
}
if normalizeLastNum {
for i in 0..<lhs.count-1 {
if lhs[i] != rhs[i] {
return (false, nil)
return (false, nil, nil)
}
}
guard let ld = Double(lhs.last!), let rd = Double(rhs.last!) else {
return (false, nil)
return (false, nil, nil)
}
if let range = numRange {
return (range.contains(ld), ld)
return (range.contains(ld), ld, extraData)
} else {
return (ld == rd, ld)
return (ld == rd, ld, extraData)
}
} else {
for i in 0..<lhs.count {
if lhs[i] != rhs[i] {
return (false, nil)
return (false, nil, nil)
}
}
}
return (true, nil)
return (true, nil, nil)
}

class MenuController: NSObject, NSMenuDelegate {
Expand Down Expand Up @@ -845,13 +874,17 @@ class MenuController: NSObject, NSMenuDelegate {
var bound = false
for kb in keyBindings {
guard kb.isIINACommand == isIINACmd else { continue }
let (sameAction, value) = sameKeyAction(kb.action, actions, normalizeLastNum, numRange)
if sameAction, let (kEqv, kMdf) = KeyCodeHelper.macOSKeyEquivalent(from: kb.rawKey) {
let (sameAction, value, extraData) = sameKeyAction(kb.action, actions, normalizeLastNum, numRange)
if sameAction, let (kEqv, kMdf) = KeyCodeHelper.macOSKeyEquivalent(from: kb.normalizedMpvKey) {
menuItem.keyEquivalent = kEqv
menuItem.keyEquivalentModifierMask = kMdf
if let value = value, let l10nKey = l10nKey {
menuItem.title = String(format: NSLocalizedString("menu." + l10nKey, comment: ""), abs(value))
menuItem.representedObject = value
if let extraData = extraData {
menuItem.representedObject = (value, extraData)
} else {
menuItem.representedObject = value
}
}
bound = true
break
Expand All @@ -865,10 +898,14 @@ class MenuController: NSObject, NSMenuDelegate {
// Need to regenerate `title` and `representedObject` from their default values.
// This is needed for the case where the menu item previously matched to a key binding, but now there is no match.
// Obviously this is a little kludgey, but it avoids having to do a big refactor and/or writing a bunch of new code.
let (_, value) = sameKeyAction(actions, actions, normalizeLastNum, numRange)
let (_, value, extraData) = sameKeyAction(actions, actions, normalizeLastNum, numRange)
if let value = value, let l10nKey = l10nKey {
menuItem.title = String(format: NSLocalizedString("menu." + l10nKey, comment: ""), abs(value))
menuItem.representedObject = value
if let extraData = extraData {
menuItem.representedObject = (value, extraData)
} else {
menuItem.representedObject = value
}
}
}
}
Expand Down

0 comments on commit e343c28

Please sign in to comment.