From 406c5898d103427193e589b4bbe615d0c739b2c5 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Tue, 02 Jun 2026 19:36:31 +0800
Subject: [PATCH] Prepare 7.8.9 tag navigation drop test package

---
 Apptag/AppDragCoordinator.swift |   51 ++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 38 insertions(+), 13 deletions(-)

diff --git a/Apptag/AppDragCoordinator.swift b/Apptag/AppDragCoordinator.swift
index e767f95..2dff55b 100644
--- a/Apptag/AppDragCoordinator.swift
+++ b/Apptag/AppDragCoordinator.swift
@@ -25,6 +25,7 @@
     private var dragWindow: NSWindow?
     private var dragImageSize: NSSize = .zero
     private var activePayload = ""
+    private weak var hoveredTarget: AppDropTargetReceivingView?
 
     private init() {}
 
@@ -129,15 +130,20 @@
             CATransaction.setDisableActions(true)
             dragLayer.position = contentPoint
             CATransaction.commit()
+            updateHoverTarget(at: screenPoint)
             return
         }
 
-        guard let dragWindow else { return }
+        guard let dragWindow else {
+            updateHoverTarget(at: screenPoint)
+            return
+        }
         let origin = NSPoint(
             x: screenPoint.x - dragImageSize.width / 2,
             y: screenPoint.y - dragImageSize.height / 2
         )
         dragWindow.setFrameOrigin(origin)
+        updateHoverTarget(at: screenPoint)
     }
 
     func finishDrag(at screenPoint: NSPoint, copy: Bool) {
@@ -147,18 +153,7 @@
         let source = parts.dropFirst().first ?? ""
         pruneDeadTargets()
 
-        let hitTarget = targets.values
-            .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
-
-        if let hitTarget {
+        if let hitTarget = dropTarget(at: screenPoint) {
             hitTarget.performDrop(path: path, source: source, copy: copy)
             return
         }
@@ -191,6 +186,8 @@
     }
 
     private func endDragVisuals() {
+        hoveredTarget?.appDragHoverChanged(active: false)
+        hoveredTarget = nil
         dragLayer?.removeFromSuperlayer()
         dragLayer = nil
         dragLayerHostView = nil
@@ -207,6 +204,29 @@
     private func pruneDeadTargets() {
         targets = targets.filter { $0.value.view != nil }
         emptyTargets = emptyTargets.filter { $0.value.view != nil }
+    }
+
+    private func updateHoverTarget(at screenPoint: NSPoint) {
+        guard hasActiveDrag else { return }
+        pruneDeadTargets()
+        let nextTarget = dropTarget(at: screenPoint)
+        guard nextTarget !== hoveredTarget else { return }
+        hoveredTarget?.appDragHoverChanged(active: false)
+        nextTarget?.appDragHoverChanged(active: true)
+        hoveredTarget = nextTarget
+    }
+
+    private func dropTarget(at screenPoint: NSPoint) -> AppDropTargetReceivingView? {
+        targets.values
+            .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
     }
 
     private static func cgImage(from image: NSImage) -> CGImage? {
@@ -261,6 +281,7 @@
 
 protocol AppDropTargetReceivingView: AnyObject {
     func screenFrame() -> NSRect?
+    func appDragHoverChanged(active: Bool)
     func performDrop(path: String, source: String, copy: Bool)
 }
 
@@ -277,6 +298,10 @@
     }
 }
 
+extension AppDropTargetReceivingView {
+    func appDragHoverChanged(active: Bool) {}
+}
+
 extension AppEmptyDropReceivingView where Self: NSView {
     func screenFrame() -> NSRect? {
         guard let window else { return nil }

--
Gitblit v1.9.3