From 0696fb405eccdd7d32e45788b008e9325cab862e Mon Sep 17 00:00:00 2001 From: Klaas Pieter Annema Date: Fri, 5 Feb 2010 11:41:20 +0100 Subject: [PATCH] improved CPTableView's drop row logic for the lowest possible row --- AppKit/CPOutlineView.j | 27 ++++- AppKit/CPTableView.j | 111 +++++++++++------- .../Manual/CPOutlineViewTest/AppController.j | 16 ++- Tests/Manual/TableTest/AppController.j | 21 +++- 4 files changed, 118 insertions(+), 57 deletions(-) diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j index f83d15721..141b7dbe8 100644 --- a/AppKit/CPOutlineView.j +++ b/AppKit/CPOutlineView.j @@ -504,11 +504,25 @@ var CPOutlineViewDelegate_outlineView_dataViewForTableColumn_item_ { var level = [self levelForRow:theLowerRow], upperLevel = [self levelForRow:theUpperRow]; - + + // If the row above us has a higher level the item can be added to multiple parent items + // Determine which one by looping through all possible parents and return the first one + // which indentation level is larger than the current x offset if (upperLevel > level) - if (theXOffset > (level + 1) * [self indentationPerLevel]) - return [self parentForItem:[self itemAtRow:theUpperRow]]; + { + while (level !== 0) + { + level = [self levelForRow:theUpperRow]; + // See if this item's indentation level matches the mouse offset + if (theXOffset > (level + 1) * [self indentationPerLevel]) + return [self parentForItem:[self itemAtRow:theUpperRow]]; + + // Check the next parent + theUpperRow = [self rowForItem:[self parentForItem:[self itemAtRow:theUpperRow]]]; + } + } + return [self parentForItem:[self itemAtRow:theLowerRow]]; } @@ -518,7 +532,7 @@ var CPOutlineViewDelegate_outlineView_dataViewForTableColumn_item_ var rect = [super _rectForDropHighlightViewBetweenUpperRow:theUpperRowIndex andLowerRow:theLowerRowIndex offset:theXOffset], parentItem = [self _parentItemForRow:theLowerRowIndex andUpperRow:theUpperRowIndex atMouseOffset:theXOffset], level = [self levelForItem:parentItem]; - + rect.origin.x = (level + 1) * [self indentationPerLevel]; return rect; @@ -851,6 +865,9 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt children = itemInfo.children; childIndex = [children indexOfObject:[_outlineView itemAtRow:theRow]]; + + if (childIndex === CPNotFound) + childIndex = children.length; } else if (theDropOperation === CPTableViewDropOn) childIndex = -1; @@ -936,7 +953,7 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt { if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_heightOfRowByItem_)) return [_outlineView._outlineViewDelegate outlineView:_outlineView heightOfRowByItem:[_outlineView itemAtRow:theRow]]; - + return [theTableView rowHeight]; } diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index e9dcf41f7..46abde0b4 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -1048,14 +1048,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; if ((_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_)) { rowHeight = [_delegate tableView:self heightOfRow:aRowIndex]; - _hasVariableRowHeight = YES; + + if (rowHeight !== _rowHeight) + _hasVariableRowHeight = YES; } else _hasVariableRowHeight = NO; // FIXME: WRONG: ASK TABLE COLUMN RANGE var previousRowRect = [self rectOfRow:aRowIndex - 1]; - return CPRectMake(0.0, CPRectGetMaxY(previousRowRect) + _intercellSpacing.height, CPRectGetWidth([self bounds]), _rowHeight); } @@ -1174,14 +1175,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; - (CPInteger)rowAtPoint:(CGPoint)aPoint { var row = -1; - - // Check if we are using variable sized rows - // to determine if we can use the quicker way to determine the row at point + + // Check if we are using variable sized rows so we can use the quicker way to determine the row at point if (!_hasVariableRowHeight) row = FLOOR(aPoint.y / (_rowHeight + _intercellSpacing.height)); else { - // We are using variable sized rows so we'll have to loop over all the rows and determine if it's at the current point + // We are using variable sized rows so we'll have to loop over all the rows and determine if it's at the point var rowArray = []; [_exposedRows getIndexes:rowArray maxCount:-1 inIndexRange:nil]; @@ -1193,15 +1193,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; if (CPRectContainsPoint([self rectOfRow:row], aPoint)) break; - - // Make sure that the row is not found if we exit the loop without breaking - row = -1; } - } - if (row >= _numberOfRows) + // Make sure we return -1 if we could not find a row row = -1; - + } + + if (row > [self numberOfRows]) + return -1; + return row; } @@ -1665,11 +1665,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; */ - (void)setDropRow:(CPInteger)row dropOperation:(CPTableViewDropOperation)operation { - if(row < 0 && operation === CPTableViewDropAbove) - row = 0; - - if(row >= [self numberOfRows] && operation === CPTableViewDropOn) - [[CPException exceptionWithName:@"Error" reason:@"Attempt to set dropRow="+ row +", dropOperation=CPTableViewDropOn when [0 - "+ [self numberOfRows] +"] is valid range of rows." userInfo:nil] raise]; + if(row > [self numberOfRows] && operation === CPTableViewDropOn) + { + var numberOfRows = [self numberOfRows] + 1; + var reason = @"Attempt to set dropRow=" + row + + " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows." + + [[CPException exceptionWithName:@"Error" reason:reason userInfo:nil] raise]; + } + _retargetedDropRow = row; _retargetedDropOperation = operation; @@ -2575,13 +2579,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; */ - (CPInteger)_proposedRowAtPoint:(CGPoint)dragPoint { - var numberOfRows = [self numberOfRows], - row = [self rowAtPoint:dragPoint]; - + var row = [self rowAtPoint:dragPoint]; + // cocoa seems to jump to the next row when we approach the below row dragPoint.y += FLOOR(CPRectGetHeight([self rectOfRow:row]) / 4.0); row = [self rowAtPoint:dragPoint]; - + return row; } @@ -2593,8 +2596,19 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; return CPDragOperationNone; } +- (CPRect)_rectForDropHighlightViewOnRow:(int)theRowIndex +{ + if (theRowIndex >= [self numberOfRows]) + theRowIndex = [self numberOfRows] - 1; + + return [self rectOfRow:theRowIndex]; +} + - (CPRect)_rectForDropHighlightViewBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex offset:(float)theXOffset { + if (theLowerRowIndex > [self numberOfRows]) + theLowerRowIndex = [self numberOfRows]; + return [self rectOfRow:theLowerRowIndex]; } @@ -2606,28 +2620,39 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; var row = [self _proposedRowAtPoint:location], dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation]; + exposedClipRect = [self exposedClipRect]; if(_retargetedDropRow !== nil) row = _retargetedDropRow; - - //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], - visibleWidth = _CGRectGetWidth(exposedClipRect); + //if the user forces -1 then we should highlight the whole tableview + // var rowRect = CPRectMakeZero(); + // if(_retargetedDropRow === -1 || row === -1) + // rowRect = [self exposedClipRect]; + // else + // rowRect = [self rectOfRow:row]; + // + // visibleWidth = _CGRectGetWidth(exposedClipRect); + // + // rowRect = _CGRectMake(_CGRectGetMinX(exposedClipRect), rowRect.origin.y, visibleWidth, rowRect.size.height); - rowRect = _CGRectMake(_CGRectGetMinX(exposedClipRect), rowRect.origin.y, visibleWidth, rowRect.size.height); + var rect = CPRectMakeZero(); + + if (row === -1) + rect = exposedClipRect; - [_dropOperationFeedbackView setDropOperation:dropOperation]; + else if (dropOperation === CPTableViewDropAbove) + rect = [self _rectForDropHighlightViewBetweenUpperRow:row - 1 andLowerRow:row offset:location.x]; + + else + rect = [self _rectForDropHighlightViewOnRow:row]; + + [_dropOperationFeedbackView setDropOperation:row !== -1 ? dropOperation : CPDragOperationNone]; [_dropOperationFeedbackView setHidden:(dragOperation == CPDragOperationNone)]; - [_dropOperationFeedbackView setFrame:[self _rectForDropHighlightViewBetweenUpperRow:row - 1 andLowerRow:row offset:location.x]]; + [_dropOperationFeedbackView setFrame:rect]; [_dropOperationFeedbackView setCurrentRow:row]; [self addSubview:_dropOperationFeedbackView]; - + // 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) [self scrollRowToVisible:row - 1]; @@ -3046,13 +3071,16 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", { if(tableView._destinationDragStyle === CPTableViewDraggingDestinationFeedbackStyleNone) return; - + var context = [[CPGraphicsContext currentContext] graphicsPort]; CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"4886ca"]); CGContextSetLineWidth(context, 3); - - if(dropOperation === CPTableViewDropOn) + + if (currentRow === -1) + CGContextStrokeRect(context, [self bounds]); + + else if(dropOperation === CPTableViewDropOn) { //if row is selected don't fill and stroke white var selectedRows = [tableView selectedRowIndexes]; @@ -3069,13 +3097,9 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", } CGContextStrokeRoundedRectangleInRect(context, newRect, 8, YES, YES, YES, YES); - } - - - if(dropOperation === CPTableViewDropAbove) + } + else if (dropOperation === CPTableViewDropAbove) { - - //reposition the view up a tad [self setFrameOrigin:CGPointMake(_frame.origin.x, _frame.origin.y - 8)]; @@ -3108,5 +3132,6 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", CGContextStrokePath(context); //CGContextStrokeLineSegments(context, [aRect.origin.x + 8, aRect.origin.y + 8, 300 , aRect.origin.y + 8]); } + } @end diff --git a/Tests/Manual/CPOutlineViewTest/AppController.j b/Tests/Manual/CPOutlineViewTest/AppController.j index 70ecefe7f..9683b8846 100644 --- a/Tests/Manual/CPOutlineViewTest/AppController.j +++ b/Tests/Manual/CPOutlineViewTest/AppController.j @@ -146,7 +146,11 @@ CustomOutlineViewDragType = @"CustomOutlineViewDragType"; [Menu menuWithTitle:@"1.1.2"], ]], [Menu menuWithTitle:@"1.2" children:[ - [Menu menuWithTitle:@"1.2.1"], + [Menu menuWithTitle:@"1.2.1" children:[ + [Menu menuWithTitle:@"1.2.1.1"], + [Menu menuWithTitle:@"1.2.1.2"], + [Menu menuWithTitle:@"1.2.1.3"], + ]], [Menu menuWithTitle:@"1.2.2"], [Menu menuWithTitle:@"1.2.3"] ]] @@ -188,6 +192,8 @@ CustomOutlineViewDragType = @"CustomOutlineViewDragType"; _outlineView = [[CPOutlineView alloc] initWithFrame:[contentView bounds]]; + + [_outlineView setBackgroundColor:[CPColor greenColor]]; var column = [[CPTableColumn alloc] initWithIdentifier:@""]; [_outlineView addTableColumn:column]; @@ -199,10 +205,14 @@ CustomOutlineViewDragType = @"CustomOutlineViewDragType"; [_outlineView expandItem:nil expandChildren:YES]; // [_outlineView setIntercellSpacing:CPSizeMake(0.0, 0.0)] - // [scrollView setDocumentView:_outlineView]; - [theWindow setContentView:_outlineView]; + [scrollView setDocumentView:_outlineView]; + [theWindow setContentView:scrollView]; + + // [theWindow setContentView:_outlineView]; [theWindow orderFront:self]; + + [column setWidth:CPRectGetWidth([_outlineView bounds])]; } - (id)outlineView:(CPOutlineView)theOutlineView child:(int)theIndex ofItem:(id)theItem diff --git a/Tests/Manual/TableTest/AppController.j b/Tests/Manual/TableTest/AppController.j index 0ca646c09..e75efcb41 100644 --- a/Tests/Manual/TableTest/AppController.j +++ b/Tests/Manual/TableTest/AppController.j @@ -22,7 +22,7 @@ CPLogRegister(CPLogConsole); dataSet1 = [], dataSet2 = []; - for(var i = 1; i < 100; i++) + for(var i = 1; i < 5; i++) { dataSet1[i - 1] = i; dataSet2[i - 1] = i + 10; @@ -217,10 +217,10 @@ CPLogRegister(CPLogConsole); } } -- (id)tableView:(CPTableView)tableView heightOfRow:(int)row -{ - return 50; -} +// - (id)tableView:(CPTableView)tableView heightOfRow:(int)row +// { +// return 50; +// } //- (void)tableViewSelectionIsChanging:(CPNotification)aNotification //{ @@ -301,10 +301,19 @@ CPLogRegister(CPLogConsole); // console.log([aTableView rectOfRow:0]); //console.log(row) + CPLog.debug(@"proposed row: %i", row); + [[aTableView window] orderFront:nil]; - if(aTableView === tableView) + if(aTableView === tableView) + { + // This is actually the behavior in Cocoa + if (row >= [aTableView numberOfRows]) + row = [aTableView numberOfRows] - 1; + [aTableView setDropRow:row dropOperation:CPTableViewDropOn]; + } + else [aTableView setDropRow:row dropOperation:CPTableViewDropAbove];