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
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