Work around a bug in NSTableView by implementing our own drawRowIndexes:clipRect: so we can stop drawing once we're outside the clipping rect. Fixes #14463
authorEvan Schoenberg
Mon, 01 Nov 2010 20:24:45 -0500
changeset 3216ab8a205c5901
parent 3215 e36d4a14d249
child 3217 6f08c2d81d16
Work around a bug in NSTableView by implementing our own drawRowIndexes:clipRect: so we can stop drawing once we're outside the clipping rect. Fixes #14463
Frameworks/AIUtilities Framework/Source/AIVariableHeightOutlineView.m
     1.1 --- a/Frameworks/AIUtilities Framework/Source/AIVariableHeightOutlineView.m	Mon Nov 01 20:23:44 2010 -0500
     1.2 +++ b/Frameworks/AIUtilities Framework/Source/AIVariableHeightOutlineView.m	Mon Nov 01 20:24:45 2010 -0500
     1.3 @@ -230,14 +230,21 @@
     1.4  	}
     1.5  }
     1.6  
     1.7 -- (void)drawRow:(NSInteger)row clipRect:(NSRect)rect
     1.8 +/*!
     1.9 + * @brief Our implementation of drawRow:clipRect: which returns whether the row was drawn
    1.10 + *
    1.11 + * This is very useful if you are iterating rows in sequence. If drawRowIndexes:clipRect: is passed 1000 rows,
    1.12 + * but row 61 is outside the clip rect, rows 62 through 1000 will also be outside the clip rect.  Although
    1.13 + * -[self rectOfRow:] is reasonable cheap once, it can get quite expensive additively. See for example #14463 (700 Twitter contacts).
    1.14 + */
    1.15 +- (BOOL)_ai_drawRow:(NSInteger)row clipRect:(NSRect)rect
    1.16  {		  
    1.17  	if (row >= 0 && row < [self numberOfRows]) { //Somebody keeps calling this method with row = numberOfRows, which is wrong.
    1.18 -
    1.19  		/* Most important optimization ever:
    1.20  		 * Only draw if some part of this row's rect is in the clip rect */
    1.21 -		if (NSIntersectsRect([self rectOfRow:row], rect) == FALSE)
    1.22 -			return;
    1.23 +		if (NSIntersectsRect([self rectOfRow:row], rect) == FALSE) {
    1.24 +			return NO;
    1.25 +		}
    1.26  		
    1.27  		NSArray		*tableColumns = [self tableColumns];
    1.28  		id			item = [self itemAtRow:row];
    1.29 @@ -271,6 +278,41 @@
    1.30  			[cell drawWithFrame:cellFrame inView:self];
    1.31  		}
    1.32  	}
    1.33 +	
    1.34 +	return YES;
    1.35 +}
    1.36 +
    1.37 +
    1.38 +- (void)drawRow:(NSInteger)row clipRect:(NSRect)rect
    1.39 +{
    1.40 +	(void)[self _ai_drawRow:row clipRect:rect];
    1.41 +}
    1.42 +
    1.43 +/*!
    1.44 + * @brief Override drawRowIndexes:clipRect: to stop when we're outside the clipRect.
    1.45 + *
    1.46 + * When -[NSTableView drawRect:] is called before any table view data is cached, it will pass every row of the table
    1.47 + * in indexes - not just those we'll soon know are outside of clipRect, which it does on subsequent calls.
    1.48 + *
    1.49 + * That's okay for a small table, but if, say, 1000 rows are in the list, it's expensive to check each one to see
    1.50 + * if it should be displayed.
    1.51 + */
    1.52 +- (void)drawRowIndexes:(NSIndexSet *)indexes clipRect:(NSRect)clipRect
    1.53 +{
    1.54 +	NSUInteger bufSize = indexes.count;
    1.55 +	NSUInteger *buf = malloc(bufSize * sizeof(NSUInteger));
    1.56 +	NSUInteger i;
    1.57 +	
    1.58 +	NSRange range = NSMakeRange(indexes.firstIndex, (indexes.lastIndex - indexes.firstIndex) + 1);
    1.59 +	[indexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
    1.60 +	
    1.61 +	for (i = 0; i != bufSize; i++) {
    1.62 +		/* draw until we're outside the clip rect.... */
    1.63 +		if (![self _ai_drawRow:buf[i] clipRect:clipRect])
    1.64 +			break;
    1.65 +	}
    1.66 +	
    1.67 +	free(buf);
    1.68  }
    1.69  
    1.70  - (NSImage *)dragImageForRows:(NSUInteger *)buf count:(unsigned int)count tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset