From f67580e2b2b34b6f816fc2bf1d74ba2524b5cc73 Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Fri, 23 Apr 2010 14:00:47 -0700 Subject: [PATCH] Add support for automatically scrolling during drag and drop operations. --- AppKit/CPClipView.j | 20 +++---- AppKit/CPDragServer.j | 121 ++++++++++++++++++++++++++++++++---------- 2 files changed, 102 insertions(+), 39 deletions(-) diff --git a/AppKit/CPClipView.j b/AppKit/CPClipView.j index 2fa6ce1ad..654cec576 100644 --- a/AppKit/CPClipView.j +++ b/AppKit/CPClipView.j @@ -190,20 +190,20 @@ - (BOOL)autoscroll:(CPEvent)anEvent { var bounds = [self bounds], - location = [self convertPoint:[event locationInWindow] fromView:nil], + eventLocation = [self convertPoint:[event locationInWindow] fromView:nil], superview = [self superview], deltaX = 0, deltaY = 0; - if (CGRectContainsPoint(bounds, location)) + if (CGRectContainsPoint(bounds, eventLocation)) return NO; if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller]) { - if (location.y < CGRectGetMinY(bounds)) - deltaY = CGRectGetMinY(bounds) - location.y; - else if (location.y > CGRectGetMaxY(bounds)) - deltaY = CGRectGetMaxY(bounds) - location.y; + if (eventLocation.y < CGRectGetMinY(bounds)) + deltaY = CGRectGetMinY(bounds) - eventLocation.y; + else if (eventLocation.y > CGRectGetMaxY(bounds)) + deltaY = CGRectGetMaxY(bounds) - eventLocation.y; if (deltaY < -bounds.size.height) deltaY = -bounds.size.height; if (deltaY > bounds.size.height) @@ -212,10 +212,10 @@ if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller]) { - if (location.x < CGRectGetMinX(bounds)) - deltaX = CGRectGetMinX(bounds) - location.x; - else if (location.x > CGRectGetMaxX(bounds)) - deltaX = CGRectGetMaxX(bounds) - location.x; + if (eventLocation.x < CGRectGetMinX(bounds)) + deltaX = CGRectGetMinX(bounds) - eventLocation.x; + else if (eventLocation.x > CGRectGetMaxX(bounds)) + deltaX = CGRectGetMaxX(bounds) - eventLocation.x; if (deltaX < -bounds.size.width) deltaX = -bounds.size.width; if (deltaX > bounds.size.width) diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j index 06f8d2435..42e03893e 100644 --- a/AppKit/CPDragServer.j +++ b/AppKit/CPDragServer.j @@ -40,29 +40,10 @@ CPDragOperationEvery = -1; #define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window]) -var CPDragServerPreviousEvent = nil, -CPDragServerAutoscrollInterval = nil; -/* -var CPDragServerAutoscroll = function() -{ - [CPDragServerSource autoscroll:CPDragServerPreviousEvent]; -} +var CPDragServerPreviousEvent = nil, + CPDragServerPeriodicUpdateInterval = 0.05; - if (CPDragServerAutoscrollInterval === nil) - { - if ([CPDragServerSource respondsToSelector:@selector(autoscroll:)]) - CPDragServerAutoscrollInterval = setInterval(CPDragServerAutoscroll, 100); - } - - CPDragServerPreviousEvent = anEvent; - - if (CPDragServerAutoscrollInterval !== nil) - clearInterval(CPDragServerAutoscrollInterval); - - CPDragServerAutoscrollInterval = nil; -*/ - -var CPSharedDragServer = nil; +var CPSharedDragServer = nil; var CPDragServerSource = nil; var CPDragServerDraggingInfo = nil; @@ -150,10 +131,13 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, CGPoint _draggingLocation; id _draggingDestination; + BOOL _draggingDestinationWantsPeriodicUpdates; CGPoint _startDragLocation; BOOL _shouldSlideBack; unsigned _dragOperation; + + CPTimer _draggingUpdateTimer; } /* @@ -234,6 +218,9 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, - (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation { + [_draggingUpdateTimer invalidate]; + _draggingUpdateTimer = nil; + var dragOperation = CPDragOperationCopy; // We have to convert base to bridge since the drag event comes from the source window, not the drag window. var draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]]; @@ -243,25 +230,89 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, if(draggingDestination !== _draggingDestination) { - if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingExited:)]) + if ([_draggingDestination respondsToSelector:@selector(draggingExited:)]) [_draggingDestination draggingExited:CPDragServerDraggingInfo]; _draggingDestination = draggingDestination; - if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingEntered:)]) + if ([_draggingDestination respondsToSelector:@selector(wantsPeriodicDraggingUpdates)]) + _draggingDestinationWantsPeriodicUpdates = [_draggingDestination wantsPeriodicDraggingUpdates]; + else + _draggingDestinationWantsPeriodicUpdates = YES; + + if ([_draggingDestination respondsToSelector:@selector(draggingEntered:)]) dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo]; } - else if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingUpdated:)]) + else if ([_draggingDestination respondsToSelector:@selector(draggingUpdated:)]) dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo]; if (!_draggingDestination) dragOperation = CPDragOperationNone; + else + { + if (_draggingDestinationWantsPeriodicUpdates) + _draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval + target:self + selector:@selector(_sendPeriodicDraggingUpdate:) + userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}] + repeats:NO]; + + var scrollView = [_draggingDestination enclosingScrollView]; + if (scrollView) + { + var contentView = [scrollView contentView], + bounds = [contentView bounds], + insetBounds = CGRectInset(bounds, 10, 10) + eventLocation = [contentView convertPoint:_draggingLocation fromView:nil], + deltaX = 0, + deltaY = 0; + + if (!CGRectContainsPoint(insetBounds, eventLocation)) + { + if ([scrollView hasVerticalScroller]) + { + if (eventLocation.y < CGRectGetMinY(insetBounds)) + deltaY = CGRectGetMinY(insetBounds) - eventLocation.y; + else if (eventLocation.y > CGRectGetMaxY(insetBounds)) + deltaY = CGRectGetMaxY(insetBounds) - eventLocation.y; + if (deltaY < -insetBounds.size.height) + deltaY = -insetBounds.size.height; + if (deltaY > insetBounds.size.height) + deltaY = insetBounds.size.height; + } + + if ([scrollView hasHorizontalScroller]) + { + if (eventLocation.x < CGRectGetMinX(insetBounds)) + deltaX = CGRectGetMinX(insetBounds) - eventLocation.x; + else if (eventLocation.x > CGRectGetMaxX(insetBounds)) + deltaX = CGRectGetMaxX(insetBounds) - eventLocation.x; + if (deltaX < -insetBounds.size.width) + deltaX = -insetBounds.size.width; + if (deltaX > insetBounds.size.width) + deltaX = insetBounds.size.width; + } + + [contentView scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)]; + } + } + } return dragOperation; } +- (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer +{ + var userInfo = [aTimer userInfo]; + _dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"] + location:[userInfo objectForKey:@"location"]]; +} + - (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation { + [_draggingUpdateTimer invalidate]; + _draggingUpdateTimer = nil; + [_draggedView removeFromSuperview]; if (![CPPlatform supportsDragAndDrop]) @@ -404,13 +455,25 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, // Stop tracking events. return; } - - [self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation]; - _dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation]; + else if (type === CPKeyDown) + { + var keyCode = [anEvent keyCode]; + if (keyCode === CPEscapeKeyCode) + { + _dragOperation = CPDragOperationNone; + [self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation]; + return; + } + } + else + { + [self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation]; + _dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation]; + } // If we're not a mouse up, then we're going to want to grab the next event. [CPApp setTarget:self selector:@selector(trackDragging:) - forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask + forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask untilDate:nil inMode:0 dequeue:NO]; }