From 8478c55e513bbbb4b1676d1d9207f7feb2509465 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Fri, 22 May 2026 00:30:57 +0800
Subject: [PATCH] Freeze 7.6.0 release candidate and move docs out of repo

---
 Apptag/AppDragCoordinator.swift |   84 ++++++++++++++++++++++++++++++------------
 1 files changed, 60 insertions(+), 24 deletions(-)

diff --git a/Apptag/AppDragCoordinator.swift b/Apptag/AppDragCoordinator.swift
index 16917b3..070e2ad 100644
--- a/Apptag/AppDragCoordinator.swift
+++ b/Apptag/AppDragCoordinator.swift
@@ -6,9 +6,8 @@
     static let shared = AppDragCoordinator()
 
     struct DropTarget {
-        weak var view: NSView?
+        weak var view: AppDropTargetReceivingView?
         var tag: String
-        var onDrop: (String, String, Bool) -> Void
     }
 
     private var targets: [UUID: DropTarget] = [:]
@@ -24,8 +23,18 @@
 
     private init() {}
 
-    func register(id: UUID, view: NSView, tag: String, onDrop: @escaping (String, String, Bool) -> Void) {
-        targets[id] = DropTarget(view: view, tag: tag, onDrop: onDrop)
+    var hasActiveDrag: Bool {
+        dragLayer != nil || dragWindow != nil || !activePayload.isEmpty
+    }
+
+    func register(id: UUID, view: AppDropTargetReceivingView, tag: String) {
+        if let existing = targets[id], existing.view === view, existing.tag == tag {
+            return
+        }
+        if targets.count > 256 {
+            pruneDeadTargets()
+        }
+        targets[id] = DropTarget(view: view, tag: tag)
     }
 
     func unregister(id: UUID) {
@@ -58,7 +67,7 @@
             panel.isFloatingPanel = true
             panel.hidesOnDeactivate = false
             panel.ignoresMouseEvents = true
-            panel.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow)))
+            panel.level = .normal
             panel.collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary, .stationary, .transient, .ignoresCycle]
             panel.isReleasedWhenClosed = false
             let contentView = NSView(frame: NSRect(origin: .zero, size: image.size))
@@ -120,16 +129,20 @@
         let parts = activePayload.components(separatedBy: "\n")
         guard let path = parts.first, !path.isEmpty else { return }
         let source = parts.dropFirst().first ?? ""
+        pruneDeadTargets()
 
         let hitTarget = targets.values
-            .compactMap { target -> (DropTarget, CGFloat)? in
-                guard let frame = target.view?.screenFrame(), frame.contains(screenPoint) else { return nil }
-                return (target, frame.width * frame.height)
+            .compactMap { target -> (AppDropTargetReceivingView, CGFloat)? in
+                guard let view = target.view,
+                      let frame = view.screenFrame(),
+                      frame.contains(screenPoint)
+                else { return nil }
+                return (view, frame.width * frame.height)
             }
             .sorted { $0.1 < $1.1 }
             .first?.0
 
-        hitTarget?.onDrop(path, source, copy)
+        hitTarget?.performDrop(path: path, source: source, copy: copy)
     }
 
     func cancelDrag() {
@@ -157,6 +170,10 @@
         dragWindow = nil
         activePayload = ""
         dragImageSize = .zero
+    }
+
+    private func pruneDeadTargets() {
+        targets = targets.filter { $0.value.view != nil }
     }
 
     private static func cgImage(from image: NSImage) -> CGImage? {
@@ -209,7 +226,12 @@
     }
 }
 
-private extension NSView {
+protocol AppDropTargetReceivingView: AnyObject {
+    func screenFrame() -> NSRect?
+    func performDrop(path: String, source: String, copy: Bool)
+}
+
+extension AppDropTargetReceivingView where Self: NSView {
     func screenFrame() -> NSRect? {
         guard let window else { return nil }
         let rectInWindow = convert(bounds, to: nil)
@@ -223,39 +245,53 @@
 
     func makeNSView(context: Context) -> AppDropTargetNSView {
         let view = AppDropTargetNSView()
-        view.targetTag = targetTag
-        view.onDropApp = onDropApp
+        view.configure(targetTag: targetTag, onDropApp: onDropApp)
         return view
     }
 
     func updateNSView(_ view: AppDropTargetNSView, context: Context) {
-        view.targetTag = targetTag
-        view.onDropApp = onDropApp
-        view.registerTarget()
+        view.configure(targetTag: targetTag, onDropApp: onDropApp)
     }
 
     static func dismantleNSView(_ view: AppDropTargetNSView, coordinator: ()) {
+        view.onDropApp = nil
         AppDragCoordinator.shared.unregister(id: view.id)
     }
 }
 
-final class AppDropTargetNSView: NSView {
+final class AppDropTargetNSView: NSView, AppDropTargetReceivingView {
     let id = UUID()
     var targetTag = ""
     var onDropApp: ((String, String, Bool) -> Void)?
 
-    override func viewDidMoveToWindow() {
-        super.viewDidMoveToWindow()
-        registerTarget()
+    func configure(targetTag: String, onDropApp: @escaping (String, String, Bool) -> Void) {
+        let tagChanged = self.targetTag != targetTag
+        self.targetTag = targetTag
+        self.onDropApp = onDropApp
+        if tagChanged {
+            registerTarget()
+        }
     }
 
-    override func layout() {
-        super.layout()
-        registerTarget()
+    override func viewDidMoveToWindow() {
+        super.viewDidMoveToWindow()
+        if window == nil {
+            AppDragCoordinator.shared.unregister(id: id)
+        } else {
+            registerTarget()
+        }
     }
 
     func registerTarget() {
-        guard window != nil, let onDropApp else { return }
-        AppDragCoordinator.shared.register(id: id, view: self, tag: targetTag, onDrop: onDropApp)
+        guard window != nil else { return }
+        AppDragCoordinator.shared.register(id: id, view: self, tag: targetTag)
+    }
+
+    func performDrop(path: String, source: String, copy: Bool) {
+        onDropApp?(path, source, copy)
+    }
+
+    deinit {
+        AppDragCoordinator.shared.unregister(id: id)
     }
 }

--
Gitblit v1.9.3