From 71bfdd4087d0b4d94bd5345cb45bb335617d737c Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 28 May 2026 15:27:05 +0800
Subject: [PATCH] Confirm single-tag removal on empty grid drop
---
Apptag/AppDragCoordinator.swift | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 47 insertions(+), 1 deletions(-)
diff --git a/Apptag/AppDragCoordinator.swift b/Apptag/AppDragCoordinator.swift
index 070e2ad..e767f95 100644
--- a/Apptag/AppDragCoordinator.swift
+++ b/Apptag/AppDragCoordinator.swift
@@ -10,7 +10,12 @@
var tag: String
}
+ struct EmptyDropTarget {
+ weak var view: AppEmptyDropReceivingView?
+ }
+
private var targets: [UUID: DropTarget] = [:]
+ private var emptyTargets: [UUID: EmptyDropTarget] = [:]
private weak var dragHostWindow: NSWindow?
private weak var dragLayerHostView: NSView?
private var dragLayer: CALayer?
@@ -39,6 +44,17 @@
func unregister(id: UUID) {
targets.removeValue(forKey: id)
+ }
+
+ func registerEmptyDropTarget(id: UUID, view: AppEmptyDropReceivingView) {
+ if emptyTargets.count > 64 {
+ pruneDeadTargets()
+ }
+ emptyTargets[id] = EmptyDropTarget(view: view)
+ }
+
+ func unregisterEmptyDropTarget(id: UUID) {
+ emptyTargets.removeValue(forKey: id)
}
func beginDrag(image: NSImage, payload: String, at screenPoint: NSPoint, copy: Bool, in hostWindow: NSWindow?) {
@@ -142,7 +158,23 @@
.sorted { $0.1 < $1.1 }
.first?.0
- hitTarget?.performDrop(path: path, source: source, copy: copy)
+ if let hitTarget {
+ hitTarget.performDrop(path: path, source: source, copy: copy)
+ return
+ }
+
+ let emptyTarget = emptyTargets.values
+ .compactMap { target -> (AppEmptyDropReceivingView, 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
+
+ emptyTarget?.performEmptyDrop(path: path, source: source, screenPoint: screenPoint, copy: copy)
}
func cancelDrag() {
@@ -174,6 +206,7 @@
private func pruneDeadTargets() {
targets = targets.filter { $0.value.view != nil }
+ emptyTargets = emptyTargets.filter { $0.value.view != nil }
}
private static func cgImage(from image: NSImage) -> CGImage? {
@@ -231,6 +264,11 @@
func performDrop(path: String, source: String, copy: Bool)
}
+protocol AppEmptyDropReceivingView: AnyObject {
+ func screenFrame() -> NSRect?
+ func performEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool)
+}
+
extension AppDropTargetReceivingView where Self: NSView {
func screenFrame() -> NSRect? {
guard let window else { return nil }
@@ -239,6 +277,14 @@
}
}
+extension AppEmptyDropReceivingView where Self: NSView {
+ func screenFrame() -> NSRect? {
+ guard let window else { return nil }
+ let rectInWindow = convert(bounds, to: nil)
+ return window.convertToScreen(rectInWindow)
+ }
+}
+
struct AppDropTargetView: NSViewRepresentable {
let targetTag: String
let onDropApp: (String, String, Bool) -> Void
--
Gitblit v1.9.3