import AppKit // MARK: - DraggableImageView Protocol protocol DraggableImageViewClickHandler: AnyObject { func thumbnailWasClicked(image: NSImage) } // MARK: - DraggableImageView class DraggableImageView: NSImageView { var onDragStart: (() -> Void)? weak var appDelegate: ScreenshotApp? private var mouseDownEvent: NSEvent? private let dragThreshold: CGFloat = 3.0 private var isPerformingDrag: Bool = false // 🎨 NEW: Track the file URL for the current image (for BGR mode) var imageURL: URL? override init(frame frameRect: NSRect) { super.init(frame: frameRect) self.imageScaling = .scaleProportionallyUpOrDown self.imageAlignment = .alignCenter self.animates = true self.imageFrameStyle = .none self.registerForDraggedTypes([.fileURL, .URL, .tiff, .png]) } required init?(coder: NSCoder) { super.init(coder: coder) self.imageScaling = .scaleProportionallyUpOrDown self.imageAlignment = .alignCenter self.animates = true self.imageFrameStyle = .none self.registerForDraggedTypes([.fileURL, .URL, .tiff, .png]) } override func mouseDown(with event: NSEvent) { self.mouseDownEvent = event self.isPerformingDrag = false } override func mouseDragged(with event: NSEvent) { guard let mouseDownEvent = self.mouseDownEvent else { super.mouseDragged(with: event) return } if !isPerformingDrag { let dragThreshold: CGFloat = 3.0 let deltaX = abs(event.locationInWindow.x - mouseDownEvent.locationInWindow.x) let deltaY = abs(event.locationInWindow.y - mouseDownEvent.locationInWindow.y) if deltaX > dragThreshold || deltaY > dragThreshold { isPerformingDrag = true self.mouseDownEvent = nil guard let unwrappedAppDelegate = appDelegate else { isPerformingDrag = false return } // 🎨 FIXED: Check for available URL (BGR mode or normal mode) before proceeding let sourceURLForDrag = self.imageURL ?? unwrappedAppDelegate.tempURL guard let finalSourceURL = sourceURLForDrag else { print("❌ DraggableImageView: No valid URL available for dragging (imageURL: \(imageURL?.path ?? "nil"), tempURL: \(unwrappedAppDelegate.tempURL?.path ?? "nil"))") isPerformingDrag = false return } print("🎯 DraggableImageView: Starting drag with URL: \(finalSourceURL.path)") if let preview = unwrappedAppDelegate.activePreviewWindow, preview.isVisible { preview.orderOut(nil as Any?) } unwrappedAppDelegate.gridViewManager?.showGrid(previewFrame: self.window?.frame) // NIEUW: Start drag session voor proximity monitoring unwrappedAppDelegate.gridViewManager?.startDragSession() let fileItem = NSDraggingItem(pasteboardWriter: finalSourceURL as NSURL) if let imageToDrag = self.image { let fullFrame = convert(bounds, to: nil) let scale: CGFloat = 0.05 let yOffset: CGFloat = 30 let scaledFrame = NSRect( x: fullFrame.midX - fullFrame.width * scale / 2, y: fullFrame.midY - fullFrame.height * scale / 2 - yOffset, width: fullFrame.width * scale, height: fullFrame.height * scale ) fileItem.setDraggingFrame(scaledFrame, contents: imageToDrag) } let items: [NSDraggingItem] = [fileItem] beginDraggingSession(with: items, event: event, source: self) } else { return } } } override func mouseUp(with event: NSEvent) { if !isPerformingDrag { if let image = self.image, let appDelegate = self.appDelegate { appDelegate.thumbnailWasClicked(image: image) } super.mouseUp(with: event) } self.mouseDownEvent = nil self.isPerformingDrag = false } } // MARK: - NSDraggingSource extension DraggableImageView: NSDraggingSource { func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation { return .copy } func draggingSession(_ session: NSDraggingSession, willBeginAt screenPoint: NSPoint) { // Drag session beginning } func draggingSession(_ session: NSDraggingSession, movedTo screenPoint: NSPoint) { guard let appDel = self.appDelegate, let gridManager = appDel.gridViewManager, let gridWindow = gridManager.gridWindow else { return } let gridFrame = gridWindow.frame let distanceToGrid = min( abs(screenPoint.x - gridFrame.minX), abs(screenPoint.x - gridFrame.maxX) ) // Update visual feedback based on proximity if Int(distanceToGrid) % 50 == 0 { let minScale: CGFloat = 0.05 let maxScale: CGFloat = 0.35 let maxDistance: CGFloat = 300 if distanceToGrid < maxDistance { let progress = distanceToGrid / maxDistance _ = minScale + (progress * (maxScale - minScale)) } } } func draggingSession(_ session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) { isPerformingDrag = false // NIEUW: Stop drag session direct na drag end appDelegate?.gridViewManager?.stopDragSession() DispatchQueue.main.async { [weak self] in guard let self = self, let appDel = self.appDelegate else { return } let didDropOnGridAction = appDel.didGridHandleDrop let didDropOnStashGridAction = appDel.didStashGridHandleDrop let didDropOnAnyGridAction = didDropOnGridAction || didDropOnStashGridAction let closeAfterDragSetting = SettingsManager.shared.closeAfterDrag if !didDropOnAnyGridAction && appDel.gridViewManager?.gridWindow != nil { appDel.gridViewManager?.hideGrid(monitorForReappear: false) } if didDropOnAnyGridAction { // Drop handled by grid action (main or stash). Preview management deferred to grid action handler. print("🔄 DraggableImageView: Grid action detected (main: \(didDropOnGridAction), stash: \(didDropOnStashGridAction))") // 🔧 CRITICAL FIX: Handle ALL grid actions (both main and stash) with closeAfterDrag setting print("🔄 Grid action completed - applying closeAfterDrag setting: \(closeAfterDragSetting)") if closeAfterDragSetting { print("🔄 Closing thumbnail after grid action due to closeAfterDrag setting") appDel.closePreviewWithAnimation(immediate: false, preserveTempFile: false) } else { print("🔄 Keeping thumbnail visible after grid action (closeAfterDrag is OFF)") appDel.ensurePreviewVisible() } } else { if operation != [] { if closeAfterDragSetting { appDel.closePreviewWithAnimation(immediate: true) } else { appDel.ensurePreviewVisible() } } else { appDel.ensurePreviewVisible() } } // Reset both flags appDel.didGridHandleDrop = false appDel.didStashGridHandleDrop = false } } } // MARK: - NSImage Extension for PNG Data extension NSImage { func pngData() -> Data? { guard let tiffData = self.tiffRepresentation, let bitmapRep = NSBitmapImageRep(data: tiffData) else { return nil } return bitmapRep.representation(using: .png, properties: [:]) } }