Sfoglia il codice sorgente

scripting: patch NSApplication to support KVO delegate

SwiftUI takes over the NSApplicationDelegate and does not pass KVO events
down to the NSApplicationDelegateAdaptor.
osy 2 anni fa
parent
commit
93e897e393
2 ha cambiato i file con 52 aggiunte e 0 eliminazioni
  1. 1 0
      Platform/macOS/UTMApp.swift
  2. 51 0
      Platform/macOS/UTMPatches.swift

+ 1 - 0
Platform/macOS/UTMApp.swift

@@ -25,6 +25,7 @@ struct UTMApp: App {
         ContentView().environmentObject(data)
         ContentView().environmentObject(data)
             .onAppear {
             .onAppear {
                 appDelegate.data = data
                 appDelegate.data = data
+                NSApp.scriptingDelegate = appDelegate
                 Task {
                 Task {
                     do {
                     do {
                         try await data.apiServer.start()
                         try await data.apiServer.start()

+ 51 - 0
Platform/macOS/UTMPatches.swift

@@ -24,6 +24,7 @@ final class UTMPatches {
     /// TODO: Some thread safety/race issues etc
     /// TODO: Some thread safety/race issues etc
     static func patchAll() {
     static func patchAll() {
         NSKeyedUnarchiver.patchToolbarItem()
         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
         // 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")
         UserDefaults.standard.set(false, forKey: "NSWindowAssertWhenDisplayCycleLimitReached")
     }
     }
@@ -56,3 +57,53 @@ extension NSKeyedUnarchiver {
               class: Self.self)
               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)
+    }
+}