mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
scripting: patch NSApplication to support KVO delegate
SwiftUI takes over the NSApplicationDelegate and does not pass KVO events down to the NSApplicationDelegateAdaptor.
This commit is contained in:
@@ -25,6 +25,7 @@ struct UTMApp: App {
|
||||
ContentView().environmentObject(data)
|
||||
.onAppear {
|
||||
appDelegate.data = data
|
||||
NSApp.scriptingDelegate = appDelegate
|
||||
Task {
|
||||
do {
|
||||
try await data.apiServer.start()
|
||||
|
||||
@@ -24,6 +24,7 @@ final class UTMPatches {
|
||||
/// TODO: Some thread safety/race issues etc
|
||||
static func patchAll() {
|
||||
NSKeyedUnarchiver.patchToolbarItem()
|
||||
NSApplication.patchApplicationScripting()
|
||||
// SwiftUI bug: works around crash due to "already had more Update Constraints in Window passes than there are views in the window" exception
|
||||
UserDefaults.standard.set(false, forKey: "NSWindowAssertWhenDisplayCycleLimitReached")
|
||||
}
|
||||
@@ -56,3 +57,53 @@ extension NSKeyedUnarchiver {
|
||||
class: Self.self)
|
||||
}
|
||||
}
|
||||
|
||||
private var ScriptingDelegateHandle: Int = 0
|
||||
|
||||
/// Patch NSApplication to use a new delegate for scripting tasks
|
||||
/// We cannot use NSApplicationDelegate because SwiftUI wraps it with its own implementation
|
||||
extension NSApplication {
|
||||
/// Set this, at startup, to the delegate used for scripting
|
||||
weak var scriptingDelegate: NSApplicationDelegate? {
|
||||
set {
|
||||
objc_setAssociatedObject(self, &ScriptingDelegateHandle, newValue, .OBJC_ASSOCIATION_ASSIGN)
|
||||
}
|
||||
|
||||
get {
|
||||
objc_getAssociatedObject(self, &ScriptingDelegateHandle) as? NSApplicationDelegate
|
||||
}
|
||||
}
|
||||
|
||||
/// Get KVO value from scripting delegate if implemented
|
||||
/// - Parameter key: Key to look up
|
||||
/// - Returns: Value from either scripting delegate or the default implementation
|
||||
@objc dynamic func xxx_value(forKey key: String) -> Any? {
|
||||
if scriptingDelegate?.application?(self, delegateHandlesKey: key) ?? false {
|
||||
return (scriptingDelegate as! NSObject).value(forKey: key)
|
||||
} else {
|
||||
return xxx_value(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get KVO value from scripting delegate if implemented
|
||||
/// - Parameters:
|
||||
/// - index: Index of item
|
||||
/// - key: Key to look up
|
||||
/// - Returns: Value from either scripting delegate or the default implementation
|
||||
@objc dynamic func xxx_value(at index: Int, inPropertyWithKey key: String) -> Any? {
|
||||
if scriptingDelegate?.application?(self, delegateHandlesKey: key) ?? false {
|
||||
return (scriptingDelegate as! NSObject).value(at: index, inPropertyWithKey: key)
|
||||
} else {
|
||||
return xxx_value(at: index, inPropertyWithKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate static func patchApplicationScripting() {
|
||||
patch(#selector(Self.value(forKey:)),
|
||||
with: #selector(Self.xxx_value(forKey:)),
|
||||
class: Self.self)
|
||||
patch(#selector(Self.value(at:inPropertyWithKey:)),
|
||||
with: #selector(Self.xxx_value(at:inPropertyWithKey:)),
|
||||
class: Self.self)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user