From e3a075a6a60bd6fee0c686e5596fffcf990528ae Mon Sep 17 00:00:00 2001 From: Klaas Pieter Annema Date: Mon, 1 Feb 2010 11:22:57 +0100 Subject: [PATCH] - implemented CPOutlineView drag & drop - improved CPTableView drag & drop feedback --- AppKit/CPOutlineView.j | 196 +++++----- AppKit/CPTableView.j | 342 ++++++++-------- .../Manual/CPOutlineViewTest/AppController.j | 364 +++++++++--------- 3 files changed, 451 insertions(+), 451 deletions(-) diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j index 75eaf6371..1a18b1866 100644 --- a/AppKit/CPOutlineView.j +++ b/AppKit/CPOutlineView.j @@ -145,7 +145,7 @@ var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_ if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:sortDescriptorsDidChange:)]) _implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_sortDescriptorsDidChange_; - [[super dataSource] setImplementedDataSourceMethods:_implementedOutlineViewDataSourceMethods]; + [[super dataSource] setImplementedDataSourceMethods:_implementedOutlineViewDataSourceMethods]; [self reloadData]; } @@ -342,41 +342,41 @@ var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_ - (void)selectRowIndexes:(CPIndexSet)rows byExtendingSelection:(BOOL)shouldExtendSelection { - // First un highlight the old disclosure controls - var previousSelectedRows = []; - [[self selectedRowIndexes] getIndexes:previousSelectedRows maxCount:-1 inIndexRange:nil]; - - var index = [previousSelectedRows count]; - while (index--) - { - var rowIndex = previousSelectedRows[index], - item = [self itemAtRow:rowIndex]; - - if (![self isExpandable:item]) - continue; - - var control = _disclosureControlsForRows[rowIndex]; - [control setHighlighted:NO]; - } - - [super selectRowIndexes:rows byExtendingSelection:shouldExtendSelection]; - - // Now highlight the new disclosure controls - var selectedRows = []; - [rows getIndexes:selectedRows maxCount:-1 inIndexRange:nil]; - - var index = [selectedRows count]; - while (index--) - { - var rowIndex = selectedRows[index], - item = [self itemAtRow:rowIndex]; - - if (![self isExpandable:item]) - continue; - - var control = _disclosureControlsForRows[rowIndex]; - [control setHighlighted:YES]; - } + // First un highlight the old disclosure controls + var previousSelectedRows = []; + [[self selectedRowIndexes] getIndexes:previousSelectedRows maxCount:-1 inIndexRange:nil]; + + var index = [previousSelectedRows count]; + while (index--) + { + var rowIndex = previousSelectedRows[index], + item = [self itemAtRow:rowIndex]; + + if (![self isExpandable:item]) + continue; + + var control = _disclosureControlsForRows[rowIndex]; + [control setHighlighted:NO]; + } + + [super selectRowIndexes:rows byExtendingSelection:shouldExtendSelection]; + + // Now highlight the new disclosure controls + var selectedRows = []; + [rows getIndexes:selectedRows maxCount:-1 inIndexRange:nil]; + + var index = [selectedRows count]; + while (index--) + { + var rowIndex = selectedRows[index], + item = [self itemAtRow:rowIndex]; + + if (![self isExpandable:item]) + continue; + + var control = _disclosureControlsForRows[rowIndex]; + [control setHighlighted:YES]; + } } - (void)setDelegate:(id)aDelegate @@ -531,12 +531,12 @@ var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_ - (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; + // 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 @@ -808,7 +808,7 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt @implementation _CPOutlineViewTableViewDataSource : CPObject { - int _implementedDataSourceMethods @accessors(property=implementedDataSourceMethods); + int _implementedDataSourceMethods @accessors(property=implementedDataSourceMethods); CPObject _outlineView; } @@ -834,70 +834,70 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt - (BOOL)tableView:(CPTableView)aTableColumn writeRowsWithIndexes:(CPIndexSet)theIndexes toPasteboard:(CPPasteboard)thePasteboard { - if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_) - return NO; - - var rowIndexes = []; - [theIndexes getIndexes:rowIndexes maxCount:[theIndexes count] inIndexRange:nil]; - - var rowIndex = [rowIndexes count], - items = []; - - while (rowIndex--) - [items addObject:[_outlineView itemAtRow:[rowIndexes objectAtIndex:rowIndex]]]; - - return [_outlineView._outlineViewDataSource outlineView:_outlineView writeItems:items toPasteboard:thePasteboard]; + if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_) + return NO; + + var rowIndexes = []; + [theIndexes getIndexes:rowIndexes maxCount:[theIndexes count] inIndexRange:nil]; + + var rowIndex = [rowIndexes count], + items = []; + + while (rowIndex--) + [items addObject:[_outlineView itemAtRow:[rowIndexes objectAtIndex:rowIndex]]]; + + return [_outlineView._outlineViewDataSource outlineView:_outlineView writeItems:items toPasteboard:thePasteboard]; } - (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id < CPDraggingInfo >)theInfo - proposedRow:(int)theRow proposedDropOperation:(CPTableViewDropOperation)theOperation + proposedRow:(int)theRow proposedDropOperation:(CPTableViewDropOperation)theOperation { - if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_) - return CPDragOperationNone; - - var droppedItem = [_outlineView itemAtRow:theRow], - parentItem = [_outlineView parentForItem:droppedItem]; - childIndex = CPNotFound; - - if (theOperation === CPTableViewDropAbove) - { - var itemInfo = (parentItem != nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo, - children = itemInfo.children, - - childIndex = [children indexOfObject:droppedItem]; - } - else if (theOperation === CPTableViewDropOn) - { - parentItem = droppedItem; - childIndex = -1; - } - - return [_outlineView._outlineViewDataSource outlineView:_outlineView validateDrop:theInfo proposedItem:parentItem proposedChildIndex:childIndex]; + if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_) + return CPDragOperationNone; + + var droppedItem = [_outlineView itemAtRow:theRow], + parentItem = [_outlineView parentForItem:droppedItem]; + childIndex = CPNotFound; + + if (theOperation === CPTableViewDropAbove) + { + var itemInfo = (parentItem != nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo, + children = itemInfo.children, + + childIndex = [children indexOfObject:droppedItem]; + } + else if (theOperation === CPTableViewDropOn) + { + parentItem = droppedItem; + childIndex = -1; + } + + return [_outlineView._outlineViewDataSource outlineView:_outlineView validateDrop:theInfo proposedItem:parentItem proposedChildIndex:childIndex]; } - (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id )theInfo row:(int)theRow dropOperation:(CPTableViewDropOperation)theOperation -{ - if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_) - return NO; - - var droppedItem = [_outlineView itemAtRow:theRow], - parentItem = [_outlineView parentForItem:droppedItem]; - childIndex = CPNotFound; +{ + if (!_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_) + return NO; + + var droppedItem = [_outlineView itemAtRow:theRow], + parentItem = [_outlineView parentForItem:droppedItem]; + childIndex = CPNotFound; - if (theOperation === CPTableViewDropAbove) - { - var itemInfo = (parentItem != nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo, - children = itemInfo.children, + if (theOperation === CPTableViewDropAbove) + { + var itemInfo = (parentItem != nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo, + children = itemInfo.children, - childIndex = [children indexOfObject:droppedItem]; - } - else if (theOperation === CPTableViewDropOn) - { - parentItem = droppedItem; - childIndex = -1; - } - - return [_outlineView._outlineViewDataSource outlineView:_outlineView acceptDrop:theInfo item:parentItem childIndex:childIndex]; + childIndex = [children indexOfObject:droppedItem]; + } + else if (theOperation === CPTableViewDropOn) + { + parentItem = droppedItem; + childIndex = -1; + } + + return [_outlineView._outlineViewDataSource outlineView:_outlineView acceptDrop:theInfo item:parentItem childIndex:childIndex]; } @end diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index eeaf4fcb9..b2902664b 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -190,7 +190,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; SEL _doubleAction; unsigned _columnAutoResizingStyle; - CPView _dropOperationFeedbackView; + CPView _dropOperationFeedbackView; // BOOL _verticalMotionCanDrag; // unsigned _destinationDragStyle; @@ -527,10 +527,10 @@ window.setTimeout(function(){ [self setSelectionHightlightColor:[CPColor selectionColorSourceView]]; _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleSourceList; } - else - { - [self setSelectionHightlightColor:[CPColor selectionColor]]; - _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular; + else + { + [self setSelectionHightlightColor:[CPColor selectionColor]]; + _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular; } } @@ -1556,63 +1556,63 @@ window.setTimeout(function(){ } - (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows - tableColumns:(CPArray)theTableColumns - event:(CPEvent)dragEvent - offset:(CPPointPointer)dragImageOffset + tableColumns:(CPArray)theTableColumns + event:(CPEvent)dragEvent + offset:(CPPointPointer)dragImageOffset { return [[CPImage alloc] initWithContentsOfFile:@"Frameworks/AppKit/Resources/GenericFile.png" size:CGSizeMake(32,32)]; } - (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows - tableColumns:(CPArray)theTableColumns - event:(CPEvent)theDragEvent - offset:(CPPoint)dragViewOffset + tableColumns:(CPArray)theTableColumns + event:(CPEvent)theDragEvent + offset:(CPPoint)dragViewOffset { - var bounds = [self bounds], - view = [[CPView alloc] initWithFrame:bounds]; - - [view setBackgroundColor:[CPColor clearColor]]; - [view setAlphaValue:0.7]; - - // We have to fetch all the data views for the selected rows and columns - // After that we can copy these add them to a transparent drag view and use that drag view - // to make it appear we are dragging images of those rows (as you would do in regular Cocoa) - var firstExposedColumn = [_exposedColumns firstIndex], - firstExposedRow = [_exposedRows firstIndex], - exposedColumnsLength = [_exposedColumns lastIndex] - firstExposedColumn + 1, - exposedRowsLength = [_exposedRows lastIndex] - firstExposedRow + 1, - columns = [], - rows = []; - - [_exposedColumns getIndexes:columns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedColumnsLength)]; + var bounds = [self bounds], + view = [[CPView alloc] initWithFrame:bounds]; + + [view setBackgroundColor:[CPColor clearColor]]; + [view setAlphaValue:0.7]; + + // We have to fetch all the data views for the selected rows and columns + // After that we can copy these add them to a transparent drag view and use that drag view + // to make it appear we are dragging images of those rows (as you would do in regular Cocoa) + var firstExposedColumn = [_exposedColumns firstIndex], + firstExposedRow = [_exposedRows firstIndex], + exposedColumnsLength = [_exposedColumns lastIndex] - firstExposedColumn + 1, + exposedRowsLength = [_exposedRows lastIndex] - firstExposedRow + 1, + columns = [], + rows = []; + + [_exposedColumns getIndexes:columns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedColumnsLength)]; [theDraggedRows getIndexes:rows maxCount:-1 inIndexRange:CPMakeRange(firstExposedRow, exposedRowsLength)]; - var columnIndex = [columns count]; - - while (columnIndex--) - { - var column = columns[columnIndex], - tableColumn = [_tableColumns objectAtIndex:column], - rowIndex = [rows count]; - - while (rowIndex--) - { - var row = rows[rowIndex]; - var dataView = [self _newDataViewForRow:row tableColumn:tableColumn]; - - [dataView setBackgroundColor:[CPColor clearColor]]; - [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]]; - [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]]; - - [view addSubview:dataView]; - } - } - - var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil]; - dragViewOffset.x = CGRectGetWidth(bounds)/2 - dragPoint.x; - dragViewOffset.y = CGRectGetHeight(bounds)/2 - dragPoint.y; - - return view; + var columnIndex = [columns count]; + + while (columnIndex--) + { + var column = columns[columnIndex], + tableColumn = [_tableColumns objectAtIndex:column], + rowIndex = [rows count]; + + while (rowIndex--) + { + var row = rows[rowIndex]; + var dataView = [self _newDataViewForRow:row tableColumn:tableColumn]; + + [dataView setBackgroundColor:[CPColor clearColor]]; + [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]]; + [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]]; + + [view addSubview:dataView]; + } + } + + var dragPoint = [self convertPoint:[theDragEvent locationInWindow] fromView:nil]; + dragViewOffset.x = CGRectGetWidth(bounds)/2 - dragPoint.x; + dragViewOffset.y = CGRectGetHeight(bounds)/2 - dragPoint.y; + + return view; } - (void)setDraggingSourceOperationMask:(CPDragOperation)mask forLocal:(BOOL)isLocal @@ -2110,7 +2110,7 @@ window.setTimeout(function(){ indexes = [], rectSelector = @selector(rectOfRow:); - [_selectionHightlightColor setFill]; + [_selectionHightlightColor setFill]; if ([_selectedRowIndexes count] >= 1) @@ -2299,11 +2299,11 @@ window.setTimeout(function(){ */ - (void)trackMouse:(CPEvent)anEvent { - // Prevent CPControl from eating the mouse events when we are in a drag session - if (![_draggedRowIndexes count]) - [super trackMouse:anEvent]; - else - [CPApp sendEvent:anEvent]; + // Prevent CPControl from eating the mouse events when we are in a drag session + if (![_draggedRowIndexes count]) + [super trackMouse:anEvent]; + else + [CPApp sendEvent:anEvent]; } /* @@ -2335,32 +2335,32 @@ window.setTimeout(function(){ if ([self canDragRowsWithIndexes:_draggedRowIndexes atPoint:aPoint] && [_dataSource tableView:self writeRowsWithIndexes:_draggedRowIndexes toPasteboard:pboard]) { - var currentEvent = [CPApp currentEvent], - offset = CPPointMakeZero(), - tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns]; - - // We deviate from the default Cocoa implementation here by asking for a view in stead of an image - // We support both, but the view prefered over the image because we can mimic the rows we are dragging - // by re-creating the data views for the dragged rows - var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes - tableColumns:tableColumns - event:currentEvent - offset:offset]; - - if (!view) - { - var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes - tableColumns:tableColumns - event:currentEvent - offset:offset]; - view = [[CPImageView alloc] initWithFrame:CPMakeRect(0, 0, [image size].width, [image size].height)]; - [view setImage:image]; - } - - var bounds = [view bounds]; - var viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds)/2 + offset.x, aPoint.y - CGRectGetHeight(bounds)/2 + offset.y); - [self dragView:view at:viewLocation offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; - + var currentEvent = [CPApp currentEvent], + offset = CPPointMakeZero(), + tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns]; + + // We deviate from the default Cocoa implementation here by asking for a view in stead of an image + // We support both, but the view prefered over the image because we can mimic the rows we are dragging + // by re-creating the data views for the dragged rows + var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes + tableColumns:tableColumns + event:currentEvent + offset:offset]; + + if (!view) + { + var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes + tableColumns:tableColumns + event:currentEvent + offset:offset]; + view = [[CPImageView alloc] initWithFrame:CPMakeRect(0, 0, [image size].width, [image size].height)]; + [view setImage:image]; + } + + var bounds = [view bounds]; + var viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds)/2 + offset.x, aPoint.y - CGRectGetHeight(bounds)/2 + offset.y); + [self dragView:view at:viewLocation offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES]; + return NO; } } @@ -2450,16 +2450,16 @@ window.setTimeout(function(){ - (CPView)_dropOperationFeedbackView { - return _dropOperationFeedbackView; + return _dropOperationFeedbackView; } - (void)_setDropOperationFeedbackView:(CPView)theFeedbackView { - if (_dropOperationFeedbackView === theFeedbackView) - return; - - [_dropOperationFeedbackView removeFromSuperview]; - _dropOperationFeedbackView = theFeedbackView; + if (_dropOperationFeedbackView === theFeedbackView) + return; + + [_dropOperationFeedbackView removeFromSuperview]; + _dropOperationFeedbackView = theFeedbackView; } /* @@ -2468,8 +2468,8 @@ window.setTimeout(function(){ - (CPDragOperation)draggingEntered:(id)sender { var location = [self convertPoint:[sender draggingLocation] fromView:nil], - dropOperation = [self _proposedDropOperationAtPoint:location], - row = [self _proposedRowAtPoint:location]; + dropOperation = [self _proposedDropOperationAtPoint:location], + row = [self _proposedRowAtPoint:location]; if(_retargetedDropRow !== nil) row = _retargetedDropRow; @@ -2492,7 +2492,7 @@ window.setTimeout(function(){ */ - (void)draggingExited:(id)sender { - [[self _dropOperationFeedbackView] setHidden:NO]; + [[self _dropOperationFeedbackView] setHidden:NO]; } /* @@ -2508,7 +2508,7 @@ window.setTimeout(function(){ _retargetedDropOperation = nil; _retargetedDropRow = nil; _draggedRowIndexes = [CPIndexSet indexSet]; - [[self _dropOperationFeedbackView] setHidden:YES]; + [[self _dropOperationFeedbackView] setHidden:YES]; } /* @ignore @@ -2523,21 +2523,21 @@ window.setTimeout(function(){ */ - (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint { - if(_retargetedDropOperation !== nil) - return _retargetedDropOperation; + if(_retargetedDropOperation !== nil) + return _retargetedDropOperation; - var row = [self rowAtPoint:theDragPoint], - rowRect = [self rectOfRow:row]; - - // If there is no (the default) or to little inter cell spacing we create some room for the CPTableViewDropAbove indicator - // This probably doesn't work if the row height is smaller than or around 5.0 - if ([self intercellSpacing].height < 5.0) - rowRect = CPRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height); - - if (CGRectContainsPoint(rowRect, theDragPoint)) - return CPTableViewDropOn; - - return CPTableViewDropAbove; + var row = [self rowAtPoint:theDragPoint], + rowRect = [self rectOfRow:row]; + + // If there is no (the default) or to little inter cell spacing we create some room for the CPTableViewDropAbove indicator + // This probably doesn't work if the row height is smaller than or around 5.0 + if ([self intercellSpacing].height < 5.0) + rowRect = CPRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height); + + if (CGRectContainsPoint(rowRect, theDragPoint)) + return CPTableViewDropOn; + + return CPTableViewDropAbove; } /* @@ -2573,54 +2573,54 @@ window.setTimeout(function(){ /*! 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 + 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; + 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 rect = CPRectMake(0.0, 0.0, CPRectGetWidth([self frame]), 10.0); - - rect.origin.y = CPRectGetMaxY(upperRowRect) - ( rect.size.height / 2.0 ); - - if (!CPSizeEqualToSize(CPSizeMakeZero(), [self intercellSpacing])) - rect.origin.y += [self intercellSpacing].height / 2.0; - - return rect; + // 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 rect = CPRectMake(0.0, 0.0, CPRectGetWidth([self frame]), 10.0); + + rect.origin.y = CPRectGetMaxY(upperRowRect) - ( rect.size.height / 2.0 ); + + if (!CPSizeEqualToSize(CPSizeMakeZero(), [self intercellSpacing])) + rect.origin.y += [self intercellSpacing].height / 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 + 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 + @param theLowerRowIndex the index of the lower row */ - (CPView)viewForDropHighlightBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex { - return [[_CPDropOperationDrawView alloc] initWithFrame: - [self rectForDropHighlightViewBetweenUpperRow:theUpperRowIndex andLowerRow:theLowerRowIndex]]; + return [[_CPDropOperationDrawView alloc] initWithFrame: + [self rectForDropHighlightViewBetweenUpperRow:theUpperRowIndex andLowerRow:theLowerRowIndex]]; } - (CPDragOperation)draggingUpdated:(id)sender { var location = [self convertPoint:[sender draggingLocation] fromView:nil], - dropOperation = [self _proposedDropOperationAtPoint:location], + dropOperation = [self _proposedDropOperationAtPoint:location], numberOfRows = [self numberOfRows]; var row = [self _proposedRowAtPoint:location], - dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation]; + dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation]; if(_retargetedDropRow !== nil) row = _retargetedDropRow; @@ -2633,33 +2633,33 @@ window.setTimeout(function(){ rowRect = [self rectOfRow:row]; var exposedClipRect = [self exposedClipRect], - visibleWidth = _CGRectGetWidth(exposedClipRect); + visibleWidth = _CGRectGetWidth(exposedClipRect); rowRect = _CGRectMake(_CGRectGetMinX(exposedClipRect), rowRect.origin.y, visibleWidth, rowRect.size.height); - - // Ask for the feedback view and cache it so we can remove it from the view hierarchy later - var dropOperationFeedbackView = nil; - - // Get the correct drop feedback view and add it to the view hierarchy - if (dropOperation === CPTableViewDropAbove) - { - dropOperationFeedbackView = [self viewForDropHighlightBetweenUpperRow:row - 1 andLowerRow:row]; - [self addSubview:dropOperationFeedbackView positioned:CPWindowAbove relativeTo:nil]; - } - else if (dropOperation === CPTableViewDropOn) - { - dropOperationFeedbackView = [self viewForDropHighlightOnRow:row]; - - // FIXME: this doesn't work for tableviews that have alternating row background colors - [self addSubview:dropOperationFeedbackView positioned:CPWindowBelow relativeTo:nil]; - } - - [self _setDropOperationFeedbackView:dropOperationFeedbackView]; - - if (CGRectIsNull([[self _dropOperationFeedbackView] frame])) - [[self _dropOperationFeedbackView] setFrame:rowRect]; - - [[self _dropOperationFeedbackView] setHidden:NO]; + + // Ask for the feedback view and cache it so we can remove it from the view hierarchy later + var dropOperationFeedbackView = nil; + + // Get the correct drop feedback view and add it to the view hierarchy + if (dropOperation === CPTableViewDropAbove) + { + dropOperationFeedbackView = [self viewForDropHighlightBetweenUpperRow:row - 1 andLowerRow:row]; + [self addSubview:dropOperationFeedbackView positioned:CPWindowAbove relativeTo:nil]; + } + else if (dropOperation === CPTableViewDropOn) + { + dropOperationFeedbackView = [self viewForDropHighlightOnRow:row]; + + // FIXME: this doesn't work for tableviews that have alternating row background colors + [self addSubview:dropOperationFeedbackView positioned:CPWindowBelow relativeTo:nil]; + } + + [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) @@ -2686,7 +2686,7 @@ window.setTimeout(function(){ - (BOOL)performDragOperation:(id)sender { var location = [self convertPoint:[sender draggingLocation] fromView:nil]; - operation = [self _proposedDropOperationAtPoint:location]; + operation = [self _proposedDropOperationAtPoint:location]; if(_retargetedDropRow !== nil) var row = _retargetedDropRow; @@ -2887,7 +2887,7 @@ window.setTimeout(function(){ var anEvent = [CPApp currentEvent]; if([[self selectedRowIndexes] count] > 0) - { + { var extend = NO; if(([anEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection) @@ -3074,19 +3074,19 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", - (void)drawRect:(CGRect)aRect { var context = [[CPGraphicsContext currentContext] graphicsPort], - rect = [self bounds]; + rect = [self bounds]; CGContextSetStrokeColor(context, [CPColor selectionColor]); CGContextSetLineWidth(context, 3.0); - // We want the ellipse to fit in a square so we make sure the width and the height of the ellipse are equal - var ellipesRect = CPRectMake(rect.origin.x + 2.5, rect.origin.y + 2.5, rect.size.height - 4.0, rect.size.height - 4.0); - CGContextStrokeEllipseInRect(context, ellipesRect); - - CGContextBeginPath(context); - CGContextMoveToPoint(context, CPRectGetMaxX(ellipesRect), CPRectGetMidY(ellipesRect)); - CGContextAddLineToPoint(context, CPRectGetMaxX(rect) - CPRectGetMaxX(ellipesRect), CPRectGetMidY(ellipesRect)); - CGContextClosePath(context); - CGContextStrokePath(context); + // We want the ellipse to fit in a square so we make sure the width and the height of the ellipse are equal + var ellipesRect = CPRectMake(rect.origin.x + 2.5, rect.origin.y + 2.5, rect.size.height - 4.0, rect.size.height - 4.0); + CGContextStrokeEllipseInRect(context, ellipesRect); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, CPRectGetMaxX(ellipesRect), CPRectGetMidY(ellipesRect)); + CGContextAddLineToPoint(context, CPRectGetMaxX(rect) - CPRectGetMaxX(ellipesRect), CPRectGetMidY(ellipesRect)); + CGContextClosePath(context); + CGContextStrokePath(context); } @end diff --git a/Tests/Manual/CPOutlineViewTest/AppController.j b/Tests/Manual/CPOutlineViewTest/AppController.j index 37d9cf451..0ecf904e9 100644 --- a/Tests/Manual/CPOutlineViewTest/AppController.j +++ b/Tests/Manual/CPOutlineViewTest/AppController.j @@ -15,278 +15,278 @@ CustomOutlineViewDragType = @"CustomOutlineViewDragType"; @implementation Menu : CPObject { - Menu _menu @accessors(property=menu); - - CPString _title @accessors(property=title); - CPArray _children @accessors(property=children); + Menu _menu @accessors(property=menu); + + CPString _title @accessors(property=title); + CPArray _children @accessors(property=children); } + (id)menuWithTitle:(CPString)theTitle { - return [[self alloc] initWithTitle:theTitle]; + return [[self alloc] initWithTitle:theTitle]; } + (id)menuWithTitle:(CPString)theTitle children:(CPArray)theChildren { - return [[self alloc] initWithTitle:theTitle children:theChildren]; + return [[self alloc] initWithTitle:theTitle children:theChildren]; } - (id)initWithTitle:(CPString)theTitle { - return [self initWithTitle:theTitle children:nil]; + return [self initWithTitle:theTitle children:nil]; } - (id)initWithTitle:(CPString)theTitle children:(CPArray)theChildren { - if ((self = [super init])) - { - _title = theTitle; - [self setChildren:theChildren]; - } - - return self; + if ((self = [super init])) + { + _title = theTitle; + [self setChildren:theChildren]; + } + + return self; } - (CPString)description { - return [self descriptionWithChildren:YES]; + return [self descriptionWithChildren:YES]; } - (CPString)descriptionWithChildren:(BOOL)showChildren { - var description = [super description] + @" " + [self title]; - - if (showChildren && [[self children] count] > 0) - description = @"\n" + [description stringByAppendingFormat:@" children: %@", [self children]]; + var description = [super description] + @" " + [self title]; + + if (showChildren && [[self children] count] > 0) + description = @"\n" + [description stringByAppendingFormat:@" children: %@", [self children]]; - return description; + return description; } - (void)insertSubmenu:(Menu)theItem atIndex:(int)theIndex { - CPLog.debug(@"insert menu: %@ in menu: %@ at index: %i", [theItem descriptionWithChildren:NO], [self descriptionWithChildren:NO], theIndex); - - if ([[self children] containsObject:theItem]) - return; - - if ([theItem menu]) - [theItem removeFromMenu]; - - [theItem setMenu:self]; - [[self children] insertObject:theItem atIndex:theIndex]; + CPLog.debug(@"insert menu: %@ in menu: %@ at index: %i", [theItem descriptionWithChildren:NO], [self descriptionWithChildren:NO], theIndex); + + if ([[self children] containsObject:theItem]) + return; + + if ([theItem menu]) + [theItem removeFromMenu]; + + [theItem setMenu:self]; + [[self children] insertObject:theItem atIndex:theIndex]; } - (void)removeFromMenu { - CPLog.debug(@"remove menu: %@ from menu: %@", [self descriptionWithChildren:NO], [[self menu] descriptionWithChildren:NO]); - - [[[self menu] children] removeObject:self]; - - CPLog.debug([[self menu] children]); - - [self setMenu:nil]; + CPLog.debug(@"remove menu: %@ from menu: %@", [self descriptionWithChildren:NO], [[self menu] descriptionWithChildren:NO]); + + [[[self menu] children] removeObject:self]; + + CPLog.debug([[self menu] children]); + + [self setMenu:nil]; } - (void)setChildren:(CPArray)theChildren { - if (_children === theChildren) - return; - - var childIndex = [theChildren count]; - while (childIndex--) - { - var child = theChildren[childIndex]; - [child setMenu:self]; - } - - _children = theChildren; + if (_children === theChildren) + return; + + var childIndex = [theChildren count]; + while (childIndex--) + { + var child = theChildren[childIndex]; + [child setMenu:self]; + } + + _children = theChildren; } - (id)initWithCoder:(CPCoder)theCoder { - if (self = [super init]) - { - _menu = [theCoder decodeObjectForKey:@"MenuSuperMenuKey"]; - _title = [theCoder decodeObjectForKey:@"MenuTitleKey"]; - [self setChildren:[theCoder decodeObjectForKey:@"MenuChildrenKey"]]; - } - - return self; + if (self = [super init]) + { + _menu = [theCoder decodeObjectForKey:@"MenuSuperMenuKey"]; + _title = [theCoder decodeObjectForKey:@"MenuTitleKey"]; + [self setChildren:[theCoder decodeObjectForKey:@"MenuChildrenKey"]]; + } + + return self; } - (void)encodeWithCoder:(CPCoder)aCoder { - [aCoder encodeObject:_menu forKey:@"MenuSuperMenuKey"]; - [aCoder encodeObject:_title forKey:@"MenuTitleKey"]; - [aCoder encodeObject:_children forKey:@"MenuChildrenKey"]; + [aCoder encodeObject:_menu forKey:@"MenuSuperMenuKey"]; + [aCoder encodeObject:_title forKey:@"MenuTitleKey"]; + [aCoder encodeObject:_children forKey:@"MenuChildrenKey"]; } @end @implementation AppController : CPObject { - Menu _menu @accessors(property=menu); - CPOutlineView _outlineView; - - CPArray _draggedItems; + Menu _menu @accessors(property=menu); + CPOutlineView _outlineView; + + CPArray _draggedItems; } - (void)applicationDidFinishLaunching:(CPNotification)aNotification { - var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], - contentView = [theWindow contentView]; + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; - _menu = [Menu menuWithTitle:@"Top" children:[ - [Menu menuWithTitle:@"1" children:[ - [Menu menuWithTitle:@"1.1" children:[ - [Menu menuWithTitle:@"1.1.1"], - [Menu menuWithTitle:@"1.1.2"], - ]], - [Menu menuWithTitle:@"1.2" children:[ - [Menu menuWithTitle:@"1.2.1"], - [Menu menuWithTitle:@"1.2.2"], - [Menu menuWithTitle:@"1.2.3"] - ]] - ]], - // [Menu menuWithTitle:@"2" children:[ - // [Menu menuWithTitle:@"2.1" children:[ - // [Menu menuWithTitle:@"2.1.1"], - // [Menu menuWithTitle:@"2.1.2"], - // [Menu menuWithTitle:@"2.1.3"], - // ]], - // [Menu menuWithTitle:@"2.2" children:[ - // [Menu menuWithTitle:@"2.2.1"], - // [Menu menuWithTitle:@"2.2.2"], - // ]] - // ]], - // [Menu menuWithTitle:@"3" children:[ - // [Menu menuWithTitle:@"3.1" children:[ - // [Menu menuWithTitle:@"3.1.1"], - // [Menu menuWithTitle:@"3.1.2"], - // [Menu menuWithTitle:@"3.1.3"], - // ]], - // [Menu menuWithTitle:@"3.2" children:[ - // [Menu menuWithTitle:@"3.2.1"], - // [Menu menuWithTitle:@"3.2.2"], - // [Menu menuWithTitle:@"3.2.3"], - // [Menu menuWithTitle:@"3.2.4"], - // ]], - // [Menu menuWithTitle:@"3.3" children:[ - // [Menu menuWithTitle:@"3.3.1"], - // [Menu menuWithTitle:@"3.3.2"], - // [Menu menuWithTitle:@"3.3.3"], - // [Menu menuWithTitle:@"3.3.4"], - // [Menu menuWithTitle:@"3.3.5"], - // ]] - // ]] - ]]; - - var scrollView = [[CPScrollView alloc] initWithFrame:[contentView bounds]]; - [theWindow setContentView:scrollView]; - - _outlineView = [[CPOutlineView alloc] initWithFrame:[contentView bounds]]; - - var column = [[CPTableColumn alloc] initWithIdentifier:@""]; - [_outlineView addTableColumn:column]; - [_outlineView setOutlineTableColumn:column]; - [_outlineView registerForDraggedTypes:[CustomOutlineViewDragType]]; - - [_outlineView setDataSource:self]; - [_outlineView setAllowsMultipleSelection:YES]; - // [_outlineView setIntercellSpacing:CPSizeMake(0.0, 0.0)] - - [scrollView setDocumentView:_outlineView]; + _menu = [Menu menuWithTitle:@"Top" children:[ + [Menu menuWithTitle:@"1" children:[ + [Menu menuWithTitle:@"1.1" children:[ + [Menu menuWithTitle:@"1.1.1"], + [Menu menuWithTitle:@"1.1.2"], + ]], + [Menu menuWithTitle:@"1.2" children:[ + [Menu menuWithTitle:@"1.2.1"], + [Menu menuWithTitle:@"1.2.2"], + [Menu menuWithTitle:@"1.2.3"] + ]] + ]], + // [Menu menuWithTitle:@"2" children:[ + // [Menu menuWithTitle:@"2.1" children:[ + // [Menu menuWithTitle:@"2.1.1"], + // [Menu menuWithTitle:@"2.1.2"], + // [Menu menuWithTitle:@"2.1.3"], + // ]], + // [Menu menuWithTitle:@"2.2" children:[ + // [Menu menuWithTitle:@"2.2.1"], + // [Menu menuWithTitle:@"2.2.2"], + // ]] + // ]], + // [Menu menuWithTitle:@"3" children:[ + // [Menu menuWithTitle:@"3.1" children:[ + // [Menu menuWithTitle:@"3.1.1"], + // [Menu menuWithTitle:@"3.1.2"], + // [Menu menuWithTitle:@"3.1.3"], + // ]], + // [Menu menuWithTitle:@"3.2" children:[ + // [Menu menuWithTitle:@"3.2.1"], + // [Menu menuWithTitle:@"3.2.2"], + // [Menu menuWithTitle:@"3.2.3"], + // [Menu menuWithTitle:@"3.2.4"], + // ]], + // [Menu menuWithTitle:@"3.3" children:[ + // [Menu menuWithTitle:@"3.3.1"], + // [Menu menuWithTitle:@"3.3.2"], + // [Menu menuWithTitle:@"3.3.3"], + // [Menu menuWithTitle:@"3.3.4"], + // [Menu menuWithTitle:@"3.3.5"], + // ]] + // ]] + ]]; + + var scrollView = [[CPScrollView alloc] initWithFrame:[contentView bounds]]; + [theWindow setContentView:scrollView]; + + _outlineView = [[CPOutlineView alloc] initWithFrame:[contentView bounds]]; + + var column = [[CPTableColumn alloc] initWithIdentifier:@""]; + [_outlineView addTableColumn:column]; + [_outlineView setOutlineTableColumn:column]; + [_outlineView registerForDraggedTypes:[CustomOutlineViewDragType]]; + + [_outlineView setDataSource:self]; + [_outlineView setAllowsMultipleSelection:YES]; + // [_outlineView setIntercellSpacing:CPSizeMake(0.0, 0.0)] + + [scrollView setDocumentView:_outlineView]; - [self expandItem:[self menu]]; + [self expandItem:[self menu]]; [theWindow orderFront:self]; } - (void)expandItem:(Menu)item { - var children = [item children], - childIndex = [children count]; - - while (childIndex--) - [self expandItem:[children objectAtIndex:childIndex]]; - - [_outlineView expandItem:item]; + var children = [item children], + childIndex = [children count]; + + while (childIndex--) + [self expandItem:[children objectAtIndex:childIndex]]; + + [_outlineView expandItem:item]; } - (id)outlineView:(CPOutlineView)theOutlineView child:(int)theIndex ofItem:(id)theItem { - if (theItem === nil) - theItem = [self menu]; - - // CPLog.debug(@"child: %i ofItem:%@ : %@", theIndex, theItem, [[theItem children] objectAtIndex:theIndex]); - - return [[theItem children] objectAtIndex:theIndex]; + if (theItem === nil) + theItem = [self menu]; + + // CPLog.debug(@"child: %i ofItem:%@ : %@", theIndex, theItem, [[theItem children] objectAtIndex:theIndex]); + + return [[theItem children] objectAtIndex:theIndex]; } - (BOOL)outlineView:(CPOutlineView)theOutlineView isItemExpandable:(id)theItem { - if (theItem === nil) - theItem = [self menu]; - - // CPLog.debug(@"isItemExpandable:%@ : %@", theItem, [[theItem children] count] > 0); - - return [[theItem children] count] > 0; + if (theItem === nil) + theItem = [self menu]; + + // CPLog.debug(@"isItemExpandable:%@ : %@", theItem, [[theItem children] count] > 0); + + return [[theItem children] count] > 0; } - (int)outlineView:(CPOutlineView)theOutlineView numberOfChildrenOfItem:(id)theItem { - if (theItem === nil) - theItem = [self menu]; - - // CPLog.debug(@"numberOfChildrenOfItem:%@ : %i", theItem, [[theItem children] count]); - - return [[theItem children] count]; + if (theItem === nil) + theItem = [self menu]; + + // CPLog.debug(@"numberOfChildrenOfItem:%@ : %i", theItem, [[theItem children] count]); + + return [[theItem children] count]; } - (id)outlineView:(CPOutlineView)anOutlineView objectValueForTableColumn:(CPTableColumn)theColumn byItem:(id)theItem -{ - if (theItem === nil) - theItem = [self menu]; - - // CPLog.debug(@"objectValueForTableColumn:%@ byItem:%@ : %@", theColumn, theItem, [theItem title]); - - return [theItem title]; +{ + if (theItem === nil) + theItem = [self menu]; + + // CPLog.debug(@"objectValueForTableColumn:%@ byItem:%@ : %@", theColumn, theItem, [theItem title]); + + return [theItem title]; } - (BOOL)outlineView:(CPOutlineView)anOutlineView writeItems:(CPArray)theItems toPasteboard:(CPPasteBoard)thePasteBoard { - _draggedItems = theItems; - [thePasteBoard declareTypes:[CustomOutlineViewDragType] owner:self]; - [thePasteBoard setData:[CPKeyedArchiver archivedDataWithRootObject:theItems] forType:CustomOutlineViewDragType]; - - return YES; + _draggedItems = theItems; + [thePasteBoard declareTypes:[CustomOutlineViewDragType] owner:self]; + [thePasteBoard setData:[CPKeyedArchiver archivedDataWithRootObject:theItems] forType:CustomOutlineViewDragType]; + + return YES; } - (CPDragOperation)outlineView:(CPOutlineView)anOutlineView validateDrop:(id < CPDraggingInfo >)theInfo proposedItem:(id)theItem proposedChildIndex:(int)theIndex { - return CPDragOperationEvery; + return CPDragOperationEvery; } - (BOOL)outlineView:(CPOutlineView)outlineView acceptDrop:(id < CPDraggingInfo >)theInfo item:(id)theItem childIndex:(int)theIndex { - if (theItem === nil) - theItem = [self menu]; - - var menuIndex = [_draggedItems count]; - while (menuIndex--) - { - var menu = [_draggedItems objectAtIndex:menuIndex]; - - // CPLog.debug(@"move item: %@ to: %@ index: %@", menu, theItem, theIndex); - - [menu removeFromMenu]; - [theItem insertSubmenu:menu atIndex:theIndex]; - theIndex += 1; - } - - CPLog.debug([[self menu] descriptionWithChildren:YES]); - - return YES; + if (theItem === nil) + theItem = [self menu]; + + var menuIndex = [_draggedItems count]; + while (menuIndex--) + { + var menu = [_draggedItems objectAtIndex:menuIndex]; + + // CPLog.debug(@"move item: %@ to: %@ index: %@", menu, theItem, theIndex); + + [menu removeFromMenu]; + [theItem insertSubmenu:menu atIndex:theIndex]; + theIndex += 1; + } + + CPLog.debug([[self menu] descriptionWithChildren:YES]); + + return YES; } @end