From 569f93549e3257af6c3439fb07b508c16d23883f Mon Sep 17 00:00:00 2001 From: daboe01 Date: Thu, 25 Dec 2025 17:23:54 +0100 Subject: [PATCH 1/2] new: slideback animation after failed drag new: slideback animation after failed drag --- AppKit/CPDragServer.j | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j index a26189618..85d1fce37 100644 --- a/AppKit/CPDragServer.j +++ b/AppKit/CPDragServer.j @@ -132,6 +132,12 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, unsigned _dragOperation; CPTimer _draggingUpdateTimer; + + // Animation Support + CGPoint _animationStartOrigin; + CGPoint _animationTargetOrigin; + CGPoint _pendingEndLocation; + CPDragOperation _pendingEndOperation; } /* @@ -325,6 +331,49 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, [_draggingUpdateTimer invalidate]; _draggingUpdateTimer = nil; + // Check if we should slide back. + // Logic: + // 1. It must be an emulated drag (controls _draggedWindow). + // 2. SlideBack was requested in dragView:... + // 3. The operation failed (CPDragOperationNone) or was cancelled. + if (![CPPlatform supportsDragAndDrop] && _shouldSlideBack && anOperation === CPDragOperationNone) + { + // Save these for the final cleanup after animation + _pendingEndLocation = aLocation; + _pendingEndOperation = anOperation; + + _animationStartOrigin = [_draggedWindow frame].origin; + _animationTargetOrigin = _startDragLocation; // Captured when drag started + + var animation = [[CPAnimation alloc] initWithDuration:0.25 animationCurve:CPAnimationEaseOut]; + [animation setDelegate:self]; + [animation startAnimation]; + + // Return early. We will call _performFinalCleanup in animationDidEnd: + return; + } + + // Normal path (Success or no slide back) + [self _performFinalCleanupWithLocation:aLocation operation:anOperation]; +} + +// Helper to interpolate the window movement manually +- (void)animation:(CPAnimation)anAnimation valueForProgress:(float)aProgress +{ + var x = _animationStartOrigin.x + (_animationTargetOrigin.x - _animationStartOrigin.x) * aProgress, + y = _animationStartOrigin.y + (_animationTargetOrigin.y - _animationStartOrigin.y) * aProgress; + + [_draggedWindow setFrameOrigin:CGPointMake(x, y)]; +} + +- (void)animationDidEnd:(CPAnimation)anAnimation +{ + [self _performFinalCleanupWithLocation:_pendingEndLocation operation:_pendingEndOperation]; +} + +// Consolidate cleanup logic to avoid duplication +- (void)_performFinalCleanupWithLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation +{ [_draggedView removeFromSuperview]; if (![CPPlatform supportsDragAndDrop]) From bc64d147a874d79e8acd48911e5172938a677013 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Thu, 25 Dec 2025 19:56:15 +0100 Subject: [PATCH 2/2] fixed: used wrong approach --- AppKit/CPDragServer.j | 48 ++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j index 85d1fce37..bd2b09a74 100644 --- a/AppKit/CPDragServer.j +++ b/AppKit/CPDragServer.j @@ -26,6 +26,7 @@ @import "CPPasteboard.j" @import "CPView.j" @import "CPWindow_Constants.j" +@import "CPViewAnimation.j" @class CPWindow // This file is imported by CPWindow.j @class _CPDOMDataTransferPasteboard @@ -132,10 +133,8 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, unsigned _dragOperation; CPTimer _draggingUpdateTimer; - - // Animation Support - CGPoint _animationStartOrigin; - CGPoint _animationTargetOrigin; + + // Animation State CGPoint _pendingEndLocation; CPDragOperation _pendingEndOperation; } @@ -331,47 +330,44 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, [_draggingUpdateTimer invalidate]; _draggingUpdateTimer = nil; - // Check if we should slide back. - // Logic: - // 1. It must be an emulated drag (controls _draggedWindow). - // 2. SlideBack was requested in dragView:... - // 3. The operation failed (CPDragOperationNone) or was cancelled. + // Check if we should slide back (drag failed + slideBack requested) if (![CPPlatform supportsDragAndDrop] && _shouldSlideBack && anOperation === CPDragOperationNone) { - // Save these for the final cleanup after animation + // Store state to finalize drag after animation completes _pendingEndLocation = aLocation; _pendingEndOperation = anOperation; - _animationStartOrigin = [_draggedWindow frame].origin; - _animationTargetOrigin = _startDragLocation; // Captured when drag started + var currentFrame = [_draggedWindow frame], + targetFrame = CGRectMake(_startDragLocation.x, _startDragLocation.y, currentFrame.size.width, currentFrame.size.height); - var animation = [[CPAnimation alloc] initWithDuration:0.25 animationCurve:CPAnimationEaseOut]; + // We use CPViewAnimation. Even though _draggedWindow is a CPWindow, + // CPViewAnimation supports targets that respond to setFrame: (like NSViewAnimation does for NSWindow). + var animation = [[CPViewAnimation alloc] initWithViewAnimations:[ + [CPDictionary dictionaryWithObjects:[_draggedWindow, currentFrame, targetFrame] + forKeys:[CPViewAnimationTargetKey, CPViewAnimationStartFrameKey, CPViewAnimationEndFrameKey]] + ]]; + + [animation setAnimationCurve:CPAnimationEaseOut]; + [animation setDuration:0.25]; [animation setDelegate:self]; [animation startAnimation]; - - // Return early. We will call _performFinalCleanup in animationDidEnd: + return; } - // Normal path (Success or no slide back) [self _performFinalCleanupWithLocation:aLocation operation:anOperation]; } -// Helper to interpolate the window movement manually -- (void)animation:(CPAnimation)anAnimation valueForProgress:(float)aProgress -{ - var x = _animationStartOrigin.x + (_animationTargetOrigin.x - _animationStartOrigin.x) * aProgress, - y = _animationStartOrigin.y + (_animationTargetOrigin.y - _animationStartOrigin.y) * aProgress; - - [_draggedWindow setFrameOrigin:CGPointMake(x, y)]; -} - - (void)animationDidEnd:(CPAnimation)anAnimation { [self _performFinalCleanupWithLocation:_pendingEndLocation operation:_pendingEndOperation]; } -// Consolidate cleanup logic to avoid duplication +- (void)animationDidStop:(CPAnimation)anAnimation +{ + [self _performFinalCleanupWithLocation:_pendingEndLocation operation:_pendingEndOperation]; +} + - (void)_performFinalCleanupWithLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation { [_draggedView removeFromSuperview];