From 99df7f6acb78c5bd8afd301ef2efa508dd338287 Mon Sep 17 00:00:00 2001 From: Klaas Pieter Annema Date: Thu, 28 Jan 2010 16:54:50 +0100 Subject: [PATCH] - further implemented CPOutlineView drag & drop support - implemented a work around in CPPlatformWindow+DOM.j for the javascript drop event never being fired --- AppKit/CPOutlineView.j | 74 ++++------ AppKit/CPTableView.j | 155 ++++++++++++++------- AppKit/Platform/DOM/CPPlatformWindow+DOM.j | 5 +- 3 files changed, 138 insertions(+), 96 deletions(-) diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j index 848312d31..c654d333e 100644 --- a/AppKit/CPOutlineView.j +++ b/AppKit/CPOutlineView.j @@ -490,47 +490,15 @@ var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_ return [super frameOfDataViewAtColumn:aColumn row:aRow]; } -// - (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint -// { -// if (!_isSelectingSession && _implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_) -// { // Start dragging of the current rows is selected or the mouse was moved enough to initiate a drag -// // This isn't the best way to do this because this requires the user to move the mouse at a certain speed -// // TODO: mouseDown currently always selects a row -// var offset = CPMakePoint(lastPoint.x - aPoint.x, ABS(lastPoint.y - aPoint.y)); -// if (offset.x > 3.0 || ([self verticalMotionCanBeginDrag] && ABS(offset.y) > 3) || ([_selectedRowIndexes containsIndex:row])) -// { -// var row = [self rowAtPoint:aPoint]; -// -// CPLog.debug(@"start drag") -// -// var draggedItems = []; -// // Check if we are dragging a selection or a single row -// if ([_selectedRowIndexes containsIndex:row]) -// { -// // Get all the items from the current selection -// var draggedIndexes = []; -// [_selectedRowIndexes getIndexes:draggedIndexes maxCount:[_selectedRowIndexes count] inIndexRange:nil] -// -// var index = [draggedIndexes count]; -// while (index--) -// [draggedItems addObject:[self itemAtRow:draggedIndexes[index]]]; -// -// var pasteboard = [CPPasteboard pasteboardWithName:CPDragPboard]; -// } -// else -// [draggedItems addObject:[self itemAtRow:row]]; -// -// // Tell the datasource to write the items to the paste board -// // We stop the drag if it's not allowed -// if (![_outlineViewDataSource writeItems:draggedIndexes toPasteboard:pasteboard]) -// return [super continueTracking:lastPoint at:aPoint]; -// -// CPLog.debug(@"dragged items: %@", draggedItems); -// } -// } -// -// return [super continueTracking:lastPoint at:aPoint]; -// } +- (CPRect)rectForDropHighlightViewBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex +{ + // Just call super and update the x to reflect the current indentation level + var level = [self levelForRow:theLowerRowIndex], + rect = [super rectForDropHighlightViewBetweenUpperRow:theUpperRowIndex andLowerRow:theLowerRowIndex]; + + rect.origin.x = level * [self indentationPerLevel]; + return rect; +} - (void)_loadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns { @@ -847,18 +815,30 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt { if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_) return CPDragOperationNone; - - return [_outlineView._outlineViewDataSource outlineView:_outlineView validateDrop:theInfo proposedItem:[_outlineView itemAtRow:theRow] proposedChildIndex:theRow]; + + var droppedItem = [_outlineView itemAtRow:theRow], + parentItem = [_outlineView parentForItem:droppedItem], + + var itemInfo = (parentItem != nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo, + children = itemInfo.children, + childIndex = [children indexOfObject:droppedItem]; + + return [_outlineView._outlineViewDataSource outlineView:_outlineView validateDrop:theInfo proposedItem:parentItem proposedChildIndex:childIndex]; } - (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id )theInfo row:(int)theRow dropOperation:(CPTableViewDropOperation)theOperation { - - CPLog.debug(@"tableview accept dorp"); if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_) return NO; - - return [_outlineView._outlineViewDataSource outlineView:_outlineView acceptDrop:theInfo item:[_outlineView itemAtRow:theRow] childIndex:0]; + + var droppedItem = [_outlineView itemAtRow:theRow], + parentItem = [_outlineView parentForItem:droppedItem], + + var itemInfo = (parentItem != nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo, + children = itemInfo.children, + childIndex = [children indexOfObject:droppedItem]; + + return [_outlineView._outlineViewDataSource outlineView:_outlineView acceptDrop:theInfo item:parentItem childIndex:childIndex]; } @end diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 72726402b..eee98fbe2 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -182,6 +182,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; SEL _doubleAction; unsigned _columnAutoResizingStyle; + CPView _dropOperationFeedbackView; + // BOOL _verticalMotionCanDrag; // unsigned _destinationDragStyle; // BOOL _isSelectingSession; @@ -249,10 +251,10 @@ window.setTimeout(function(){ self._retargetedDropOperation = nil; self._dragOperationDefaultMask = nil; self._destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular; - self._dropOperationFeedbackView = [[_dropOperationDrawingView alloc] initWithFrame:_CGRectMakeZero()]; - [self addSubview:_dropOperationFeedbackView]; - [_dropOperationFeedbackView setHidden:YES]; - [_dropOperationFeedbackView setTableView:self]; + // self._dropOperationFeedbackView = [[_dropOperationDrawingView alloc] initWithFrame:_CGRectMakeZero()]; + // [self addSubview:_dropOperationFeedbackView]; + // [_dropOperationFeedbackView setHidden:YES]; + // [_dropOperationFeedbackView setTableView:self]; },0); _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self]; @@ -1535,8 +1537,6 @@ window.setTimeout(function(){ return _verticalMotionCanDrag; } - - //Sorting /* * - setSortDescriptors: @@ -2297,18 +2297,29 @@ window.setTimeout(function(){ [self sendAction:_doubleAction to:_target]; } +- (CPView)_dropOperationFeedbackView +{ + return _dropOperationFeedbackView; +} + +- (void)_setDropOperationFeedbackView:(CPView)theFeedbackView +{ + if (_dropOperationFeedbackView === theFeedbackView) + return; + + [_dropOperationFeedbackView removeFromSuperview]; + _dropOperationFeedbackView = theFeedbackView; + [self addSubview:_dropOperationFeedbackView positioned:CPWindowBelow relativeTo:nil]; +} + /* @ignore */ - (CPDragOperation)draggingEntered:(id)sender { - var dropOperation = [self _proposedDropOperation], - draggingLocation = [sender draggingLocation], - row; - - var location = [self convertPoint:draggingLocation fromView:nil]; - - row = [self _proposedRowAtPoint:location]; + var location = [self convertPoint:[sender draggingLocation] fromView:nil], + dropOperation = [self _proposedDropOperationAtPoint:location], + row = [self _proposedRowAtPoint:location]; if(_retargetedDropRow !== nil) row = _retargetedDropRow; @@ -2331,7 +2342,7 @@ window.setTimeout(function(){ */ - (void)draggingExited:(id)sender { - [_dropOperationFeedbackView setHidden:YES]; + [[self _dropOperationFeedbackView] setHidden:NO]; } /* @@ -2347,7 +2358,7 @@ window.setTimeout(function(){ _retargetedDropOperation = nil; _retargetedDropRow = nil; _draggedRowIndexes = [CPIndexSet indexSet]; - [_dropOperationFeedbackView setHidden:YES]; + [[self _dropOperationFeedbackView] setHidden:YES]; } /* @ignore @@ -2360,14 +2371,19 @@ window.setTimeout(function(){ /* @ignore */ -- (CPTableViewDropOperation)_proposedDropOperation +- (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint { - //check is something is forced... - // otherwise we use the above action by default - if(_retargetedDropOperation !== nil) - return _retargetedDropOperation; - else - return CPTableViewDropAbove; + if(_retargetedDropOperation !== nil) + return _retargetedDropOperation; + + + var row = [self rowAtPoint:theDragPoint], + rowRect = [self rectOfRow:row]; + + if (CGRectContainsPoint(rowRect, theDragPoint)) + return CPTableViewDropOn; + + return CPTableViewDropAbove; } /* @@ -2382,7 +2398,7 @@ window.setTimeout(function(){ if (dragPoint.y > numberOfRows * (_rowHeight + _intercellSpacing.height)) { - if ([self _proposedDropOperation] === CPTableViewDropAbove) + if ([self _proposedDropOperationAtPoint:dragPoint] === CPTableViewDropAbove) row = numberOfRows; else row = numberOfRows - 1; @@ -2401,39 +2417,86 @@ window.setTimeout(function(){ return CPDragOperationNone; } +/*! + Returns the subview that will draw the drop highlight on the row. + Sublcasses can override this to return a custom view to draw their drop highlight + @param theRowIndex the row index that should be highlighted +*/ +- (CPView)viewForDropHighlightOnRow:(int)theRowIndex +{ + var view = [[CPView alloc] initWithFrame:[self rectOfRow:theRowIndex]]; + [view setBackgroundColor:[CPColor colorWithRed:175.0 / 255.0 green:193.0 / 255.0 blue:220.0 / 255.0 alpha:1.0]]; + return view; +} + +- (CPRect)rectForDropHighlightViewBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex +{ + // The default table view implemenation does not use the offset so we just place the view at x 0.0 + var upperRowRect = [self rectOfRow:theUpperRowIndex], + lowerRowRect = [self rectOfRow:theLowerRowIndex]; + + // Place the highlight view in the middle of the rows or in the middle of the intercell spacing + // TODO: this currently looks off because the row highlights and labels are not drawn in the middle of the row + var yLocation = CPRectGetMaxY(upperRowRect) + [self intercellSpacing].height / 2.0 - 1.0, + rect = CPRectMake(0.0, + yLocation, + CPRectGetWidth([self frame]), + 2.0); + + return rect; +} + +/*! + Returns the subview that will draw the drop highlight between the rows. + Sublcasses can override this to return a custom view to draw their drop highlight + @param theUpperRowIndex the index of the upper row + @param theLowerRowIndex the index of the lower row +*/ +- (CPView)viewForDropHighlightBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex +{ + var view = [[CPView alloc] initWithFrame:[self rectForDropHighlightViewBetweenUpperRow:theUpperRowIndex andLowerRow:theLowerRowIndex]]; + [view setBackgroundColor:[CPColor greenColor]]; + return view; +} + - (CPDragOperation)draggingUpdated:(id)sender { - var dropOperation = [self _proposedDropOperation], - numberOfRows = [self numberOfRows], - draggingLocation = [sender draggingLocation], - dragOperation, - row; + var location = [self convertPoint:[sender draggingLocation] fromView:nil], + dropOperation = [self _proposedDropOperationAtPoint:location], + numberOfRows = [self numberOfRows]; - var location = [self convertPoint:draggingLocation fromView:nil]; - - row = [self _proposedRowAtPoint:location]; - dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation]; + var row = [self _proposedRowAtPoint:location], + dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation]; if(_retargetedDropRow !== nil) row = _retargetedDropRow; - //if the user forces -1 then we should highlight the whole tabelview - var rowRect; + //if the user forces -1 then we should highlight the whole tableview + var rowRect = CPRectMakeZero(); if(_retargetedDropRow === -1) rowRect = [self exposedClipRect]; else rowRect = [self rectOfRow:row]; - var exposedClipRect = [self exposedClipRect]; - var visibleWidth = _CGRectGetWidth(exposedClipRect); + var exposedClipRect = [self exposedClipRect], + visibleWidth = _CGRectGetWidth(exposedClipRect); rowRect = _CGRectMake(_CGRectGetMinX(exposedClipRect), rowRect.origin.y, visibleWidth, rowRect.size.height); - - [_dropOperationFeedbackView setDropOperation:dropOperation]; - [_dropOperationFeedbackView setHidden:(dragOperation == CPDragOperationNone)]; - [_dropOperationFeedbackView setFrame:rowRect]; - [_dropOperationFeedbackView setCurrentRow:row]; - [self addSubview:_dropOperationFeedbackView]; + + // Ask for the feedback view and cache it so we can remove it from the view hierarchy later + var dropOperationFeedbackView = nil; + + if (dropOperation === CPTableViewDropAbove) + dropOperationFeedbackView = [self viewForDropHighlightBetweenUpperRow:row - 1 andLowerRow:row]; + else if (dropOperation === CPTableViewDropOn) + dropOperationFeedbackView = [self viewForDropHighlightOnRow:row]; + + [self _setDropOperationFeedbackView:dropOperationFeedbackView]; + + if (CGRectIsNull([[self _dropOperationFeedbackView] frame])) + [[self _dropOperationFeedbackView] setFrame:rowRect]; + + [[self _dropOperationFeedbackView] setHidden:NO]; // FIXME : Maybe we should do this in a timer outside this method. Problem: we don't know when the scroll ends and neighter when the next -draggingUpdated is called. Which one will come first ? if (row > 0 && location.y - CGRectGetMinY(exposedClipRect) < _rowHeight) @@ -2451,8 +2514,6 @@ window.setTimeout(function(){ { // FIX ME: is there anything else that needs to happen here? // actual validation is called in dragginUpdated: - [_dropOperationFeedbackView setHidden:YES]; - return (_implementedDataSourceMethods & CPTableViewDataSource_tableView_validateDrop_proposedRow_proposedDropOperation_); } @@ -2461,10 +2522,8 @@ window.setTimeout(function(){ */ - (BOOL)performDragOperation:(id)sender { - var operation = [self _proposedDropOperation], - draggingLocation = [sender draggingLocation]; - - var location = [self convertPoint:draggingLocation fromView:nil]; + var location = [self convertPoint:[sender draggingLocation] fromView:nil]; + operation = [self _proposedDropOperationAtPoint:location], if(_retargetedDropRow !== nil) var row = _retargetedDropRow; diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j index 152373698..b5128896b 100644 --- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j +++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j @@ -549,7 +549,10 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop]; dragOperation = CPDragOperationLink; else dragOperation = CPDragOperationNone; - + + // Temporary hack to work around the 'drop' event type never being fired + [dragServer performDragOperationInPlatformWindow:self]; + [dragServer draggingEndedInPlatformWindow:self globalLocation:[CPPlatform isBrowser] ? location : _CGPointMake(aDOMEvent.screenX, aDOMEvent.screenY) operation:dragOperation]; }