diff --git a/AppKit/CPArrayController.j b/AppKit/CPArrayController.j index 0d9fe2b49..42ad4acc3 100644 --- a/AppKit/CPArrayController.j +++ b/AppKit/CPArrayController.j @@ -719,8 +719,12 @@ if (![self canAdd]) return; - if (_clearsFilterPredicateOnInsertion) + var willClearPredicate = NO; + if (_clearsFilterPredicateOnInsertion && _filterPredicate) + { [self willChangeValueForKey:@"filterPredicate"]; + willClearPredicate = YES; + } [self willChangeValueForKey:@"content"]; @@ -735,11 +739,15 @@ [_contentObject addObject:object]; _disableSetContent = NO; - if (_clearsFilterPredicateOnInsertion) - [self __setFilterPredicate:nil]; - - if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object]) + if (willClearPredicate) { + // Full rearrange needed due to changed filter. + _filterPredicate = nil; + [self _rearrangeObjects]; + } + else if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object]) + { + // Insert directly into the array. var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors]; // selectionIndexes change notification will be fired as a result of the @@ -749,12 +757,16 @@ else [_selectionIndexes shiftIndexesStartingAtIndex:pos by:1]; } - else - [self _rearrangeObjects]; + /* + else if (_filterPredicate !== nil) + ... + // Implies _filterPredicate && ![_filterPredicate evaluateWithObject:object], so the new object does + // not appear in arrangedObjects and we do not have to update at all. + */ // This will also send notificaitons for arrangedObjects. [self didChangeValueForKey:@"content"]; - if (_clearsFilterPredicateOnInsertion) + if (willClearPredicate) [self didChangeValueForKey:@"filterPredicate"]; } diff --git a/Tests/AppKit/CPArrayControllerPerformance.j b/Tests/AppKit/CPArrayControllerPerformance.j index 0e73ffffd..98137b1ee 100644 --- a/Tests/AppKit/CPArrayControllerPerformance.j +++ b/Tests/AppKit/CPArrayControllerPerformance.j @@ -94,7 +94,7 @@ var ELEMENTS = 200, ]]; var start = (new Date).getTime(); - for (var i = 0; i < REPEATS; i++) + for (var i = 0; i < REPEATS / 2; i++) { [ac setFilterPredicate:predicate]; [ac addObject:[Sortable sortableWithA:i B:i * 2]]; @@ -112,6 +112,30 @@ var ELEMENTS = 200, } var end = (new Date).getTime(); CPLog.warn("testAddObject_, sorted, clear filter on insert: " + (end - start) + "ms"); + + [ac setClearsFilterPredicateOnInsertion:NO]; + [ac setFilterPredicate:predicate]; + + var start = (new Date).getTime(); + for (var i = 0; i < REPEATS; i++) + { + [ac addObject:[Sortable sortableWithA:i B:i % 3]]; + + var sorted = [ac arrangedObjects], + last = ELEMENTS; + + // Verify that all is well. + for (var j = 0, count = [sorted count]; j < count; j++) + { + if (sorted[j].b == 0) + [self fail:"b == 0 should be filtered out (position: " + j + ")"]; + if (sorted[j].a >= last) + [self fail:"array values should be descending (position: " + j + ")"]; + last = sorted[j]; + } + } + var end = (new Date).getTime(); + CPLog.warn("testAddObject_, sorted, filtered: " + (end - start) + "ms"); } @end