From 77896f65b92c756da699e84499b180d35b0f8835 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 25 Jun 2026 18:52:06 +0800
Subject: [PATCH] Merge dark glass 8.0.5 freeze
---
src/Apptag/AppDragCoordinator.swift | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 174 insertions(+), 1 deletions(-)
diff --git a/src/Apptag/AppDragCoordinator.swift b/src/Apptag/AppDragCoordinator.swift
index 264706d..c7abcc8 100644
--- a/src/Apptag/AppDragCoordinator.swift
+++ b/src/Apptag/AppDragCoordinator.swift
@@ -21,6 +21,9 @@
private var dragLayer: CALayer?
private var normalDragImage: CGImage?
private var copyDragImage: CGImage?
+ private var shredPreviewActive = false
+ private var shredSliceLayers: [CALayer] = []
+ private var shredGuideLayers: [CALayer] = []
private var currentCopyMode = false
private var dragWindow: NSWindow?
private var dragImageSize: NSSize = .zero
@@ -131,6 +134,7 @@
dragLayer.position = contentPoint
CATransaction.commit()
updateHoverTarget(at: screenPoint)
+ updateRemoveTagPreview(at: screenPoint)
hoveredTarget?.appDragLocationChanged(screenPoint: screenPoint, copy: currentCopyMode)
return
}
@@ -145,6 +149,7 @@
)
dragWindow.setFrameOrigin(origin)
updateHoverTarget(at: screenPoint)
+ updateRemoveTagPreview(at: screenPoint)
hoveredTarget?.appDragLocationChanged(screenPoint: screenPoint, copy: currentCopyMode)
}
@@ -190,11 +195,17 @@
currentCopyMode = copy
CATransaction.begin()
CATransaction.setDisableActions(true)
- dragLayer?.contents = copy ? copyDragImage : normalDragImage
+ if shredPreviewActive {
+ let image = currentDragImage
+ shredSliceLayers.forEach { $0.contents = image }
+ } else {
+ dragLayer?.contents = currentDragImage
+ }
CATransaction.commit()
}
private func endDragVisuals() {
+ stopShredPreview()
hoveredTarget?.appDragHoverChanged(active: false)
hoveredTarget = nil
dragLayer?.removeFromSuperlayer()
@@ -210,6 +221,10 @@
dragImageSize = .zero
}
+ private var currentDragImage: CGImage? {
+ currentCopyMode ? copyDragImage : normalDragImage
+ }
+
private func pruneDeadTargets() {
targets = targets.filter { $0.value.view != nil }
emptyTargets = emptyTargets.filter { $0.value.view != nil }
@@ -223,6 +238,157 @@
hoveredTarget?.appDragHoverChanged(active: false)
nextTarget?.appDragHoverChanged(active: true)
hoveredTarget = nextTarget
+ }
+
+ private func updateRemoveTagPreview(at screenPoint: NSPoint) {
+ guard hasActiveDrag else {
+ setShredPreviewActive(false)
+ return
+ }
+ setShredPreviewActive(removeTagPreviewTarget(at: screenPoint) != nil)
+ }
+
+ private func removeTagPreviewTarget(at screenPoint: NSPoint) -> AppEmptyDropReceivingView? {
+ guard dropTarget(at: screenPoint) == nil else { return nil }
+ let parts = activePayload.components(separatedBy: "\n")
+ guard let path = parts.first, !path.isEmpty else { return nil }
+ let source = parts.dropFirst().first ?? ""
+
+ return emptyTargets.values
+ .compactMap { target -> (AppEmptyDropReceivingView, CGFloat)? in
+ guard let view = target.view,
+ let frame = view.screenFrame(),
+ frame.contains(screenPoint),
+ view.canPreviewEmptyDrop(
+ path: path,
+ source: source,
+ screenPoint: screenPoint,
+ copy: currentCopyMode
+ )
+ else { return nil }
+ return (view, frame.width * frame.height)
+ }
+ .sorted { $0.1 < $1.1 }
+ .first?.0
+ }
+
+ private func setShredPreviewActive(_ active: Bool) {
+ guard active != shredPreviewActive else { return }
+ if active {
+ startShredPreview()
+ } else {
+ stopShredPreview()
+ }
+ }
+
+ private func startShredPreview() {
+ guard let dragLayer,
+ let image = currentDragImage,
+ !shredPreviewActive,
+ dragImageSize.width > 0,
+ dragImageSize.height > 0
+ else { return }
+
+ shredPreviewActive = true
+ dragLayer.contents = nil
+ shredSliceLayers.removeAll()
+ shredGuideLayers.removeAll()
+
+ let sliceCount = 7
+ let sliceWidth = dragImageSize.width / CGFloat(sliceCount)
+ for index in 0..<sliceCount {
+ let x = CGFloat(index) * sliceWidth
+ let width = index == sliceCount - 1
+ ? dragImageSize.width - x
+ : ceil(sliceWidth)
+ let slice = CALayer()
+ slice.frame = CGRect(x: x, y: 0, width: width, height: dragImageSize.height)
+ slice.contents = image
+ slice.contentsGravity = .resize
+ slice.contentsScale = NSScreen.main?.backingScaleFactor ?? 2
+ slice.contentsRect = CGRect(
+ x: CGFloat(index) / CGFloat(sliceCount),
+ y: 0,
+ width: 1 / CGFloat(sliceCount),
+ height: 1
+ )
+ slice.actions = [
+ "position": NSNull(),
+ "opacity": NSNull(),
+ "transform": NSNull()
+ ]
+ dragLayer.addSublayer(slice)
+ addShredAnimation(to: slice, index: index)
+ shredSliceLayers.append(slice)
+
+ if index > 0 {
+ let guide = CALayer()
+ guide.frame = CGRect(
+ x: x - 0.5,
+ y: dragImageSize.height * 0.12,
+ width: 1,
+ height: dragImageSize.height * 0.76
+ )
+ guide.backgroundColor = NSColor.white.withAlphaComponent(0.42).cgColor
+ guide.opacity = 0
+ guide.actions = [
+ "opacity": NSNull(),
+ "position": NSNull()
+ ]
+ dragLayer.addSublayer(guide)
+ addShredGuideAnimation(to: guide, index: index)
+ shredGuideLayers.append(guide)
+ }
+ }
+ }
+
+ private func stopShredPreview() {
+ guard shredPreviewActive || !shredSliceLayers.isEmpty || !shredGuideLayers.isEmpty else { return }
+ shredPreviewActive = false
+ shredSliceLayers.forEach {
+ $0.removeAllAnimations()
+ $0.removeFromSuperlayer()
+ }
+ shredGuideLayers.forEach {
+ $0.removeAllAnimations()
+ $0.removeFromSuperlayer()
+ }
+ shredSliceLayers.removeAll()
+ shredGuideLayers.removeAll()
+ dragLayer?.contents = currentDragImage
+ }
+
+ private func addShredAnimation(to layer: CALayer, index: Int) {
+ let direction: CGFloat = index.isMultiple(of: 2) ? 1 : -1
+ let travel = min(4.5, max(2.0, dragImageSize.height * 0.06))
+ let drift = min(2.4, max(1.0, dragImageSize.width * 0.025)) * direction
+
+ let y = CAKeyframeAnimation(keyPath: "transform.translation.y")
+ y.values = [0, -travel, travel * 0.28, -travel * 0.52, 0]
+
+ let x = CAKeyframeAnimation(keyPath: "transform.translation.x")
+ x.values = [0, drift, -drift * 0.45, drift * 0.25, 0]
+
+ let opacity = CAKeyframeAnimation(keyPath: "opacity")
+ opacity.values = [1, 0.82, 0.96, 0.88, 1]
+
+ let group = CAAnimationGroup()
+ group.animations = [x, y, opacity]
+ group.duration = 0.46
+ group.repeatCount = .greatestFiniteMagnitude
+ group.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
+ group.beginTime = CACurrentMediaTime() + Double(index) * 0.018
+ layer.add(group, forKey: "removeTagShredPreview")
+ }
+
+ private func addShredGuideAnimation(to layer: CALayer, index: Int) {
+ let opacity = CAKeyframeAnimation(keyPath: "opacity")
+ opacity.values = [0, 0.42, 0.18, 0.35, 0]
+ opacity.duration = 0.46
+ opacity.repeatCount = .greatestFiniteMagnitude
+ opacity.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
+ opacity.beginTime = CACurrentMediaTime() + Double(index) * 0.018
+ layer.add(opacity, forKey: "removeTagShredGuide")
}
private func dropTarget(at screenPoint: NSPoint) -> AppDropTargetReceivingView? {
@@ -298,6 +464,7 @@
protocol AppEmptyDropReceivingView: AnyObject {
func screenFrame() -> NSRect?
+ func canPreviewEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool) -> Bool
func performEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool)
}
@@ -325,6 +492,12 @@
}
}
+extension AppEmptyDropReceivingView {
+ func canPreviewEmptyDrop(path: String, source: String, screenPoint: NSPoint, copy: Bool) -> Bool {
+ true
+ }
+}
+
struct AppDropTargetView: NSViewRepresentable {
let targetTag: String
let onDropApp: (String, String, Bool) -> Void
--
Gitblit v1.9.3