diff --git a/AppKit/CPArrayController.j b/AppKit/CPArrayController.j index 6681a768b..2b59e448f 100644 --- a/AppKit/CPArrayController.j +++ b/AppKit/CPArrayController.j @@ -394,7 +394,8 @@ // Push back the new selection to the model for selectionIndexes if we have one. // There won't be an infinite loop because of the equality check above. - [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"]; + var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"]; + [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"]; return YES; } diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j index d73cf6c0f..672991207 100644 --- a/AppKit/CPButton.j +++ b/AppKit/CPButton.j @@ -203,7 +203,7 @@ CPButtonImageOffset = 3.0; else if (![anObjectValue isKindOfClass:[CPNumber class]]) anObjectValue = CPOnState; - else if (anObjectValue > CPOnState) + else if (anObjectValue >= CPOnState) anObjectValue = CPOnState else if (anObjectValue < CPOffState) @@ -255,6 +255,9 @@ CPButtonImageOffset = 3.0; */ - (void)setNextState { + if ([self infoForBinding:CPValueBinding]) + [self setAllowsMixedState:NO]; + [self setState:[self nextState]]; } diff --git a/AppKit/CPCheckBox.j b/AppKit/CPCheckBox.j index 9e3a5ebb3..887126aaf 100644 --- a/AppKit/CPCheckBox.j +++ b/AppKit/CPCheckBox.j @@ -44,6 +44,14 @@ CPCheckBoxImageOffset = 4.0; return @"check-box"; } ++ (Class)_binderClassForBinding:(CPString)theBinding +{ + if (theBinding === CPValueBinding) + return [_CPCheckBoxValueBinder class]; + + return [super _binderClassForBinding:theBinding]; +} + - (id)initWithFrame:(CGRect)aFrame { self = [super initWithFrame:aFrame]; @@ -87,3 +95,53 @@ CPCheckBoxImageOffset = 4.0; } @end + +@implementation _CPCheckBoxValueBinder : CPBinder +{ +} + +- (void)setValueFor:(CPString)theBinding +{ + var destination = [_info objectForKey:CPObservedObjectKey], + keyPath = [_info objectForKey:CPObservedKeyPathKey], + options = [_info objectForKey:CPOptionsKey], + newValue = [destination valueForKeyPath:keyPath], + isPlaceholder = CPIsControllerMarker(newValue); + + if (isPlaceholder) + { + switch (newValue) + { + case CPMultipleValuesMarker: + newValue = CPMixedState; + break; + + case CPNoSelectionMarker: + newValue = CPOffState; + break; + + case CPNotApplicableMarker: + if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption]) + [CPException raise:CPGenericException reason:@"can't transform non applicable key on: "+_source+" value: "+newValue]; + + newValue = CPOffState; + break; + } + + if (newValue === CPMixedState) + { + [_source setAllowsMixedState:YES]; + } + else + { + // Cocoa will always set allowsMixedState to NO + // This behavior will be fine for Cappuccino as well if we (like Cocoa) + // default the CPConditionallySetsEnabledBindingOption to YES + [_source setAllowsMixedState:NO]; + } + } + + [_source setState:newValue]; +} + +@end diff --git a/AppKit/CPCollectionView.j b/AppKit/CPCollectionView.j index 43cabc0f6..2ac7d0b86 100644 --- a/AppKit/CPCollectionView.j +++ b/AppKit/CPCollectionView.j @@ -312,7 +312,8 @@ while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound) [_items[index] setSelected:YES]; - [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"]; + var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"]; + [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"]; if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)]) [_delegate collectionViewDidChangeSelection:self]; diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j index 37ba2421a..806091ed4 100644 --- a/AppKit/CPControl.j +++ b/AppKit/CPControl.j @@ -141,18 +141,20 @@ var CPControlBlackColor = [CPColor blackColor]; } } -- (void)_reverseSetBinding ++ (Class)_binderClassForBinding:(CPString)theBinding { - var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self]; - [theBinding reverseSetValueFor:@"objectValue"]; + if (theBinding === CPValueBinding) + return [_CPValueBinder class]; + + return [super _binderClassForBinding:theBinding]; } -- (void)_replacementKeyPathForBinding:(CPString)aBinding +- (void)_reverseSetBinding { - if (aBinding === @"value") - return @"objectValue"; + var binderClass = [[self class] _binderClassForBinding:CPValueBinding], + theBinding = [binderClass getBinding:CPValueBinding forObject:self]; - return [super _replacementKeyPathForBinding:aBinding]; + [theBinding reverseSetValueFor:@"objectValue"]; } - (id)initWithFrame:(CGRect)aFrame diff --git a/AppKit/CPKeyValueBinding.j b/AppKit/CPKeyValueBinding.j index 409ee7c14..078d0bcdd 100644 --- a/AppKit/CPKeyValueBinding.j +++ b/AppKit/CPKeyValueBinding.j @@ -35,7 +35,7 @@ var exposedBindingsMap = [CPDictionary new], var CPBindingOperationAnd = 0, CPBindingOperationOr = 1; -@implementation CPKeyValueBinding : CPObject +@implementation CPBinder : CPObject { CPDictionary _info; id _source; @@ -59,7 +59,7 @@ var CPBindingOperationAnd = 0, return [[exposedBindingsMap objectForKey:[aClass UID]] copy]; } -+ (CPKeyValueBinding)getBinding:(CPString)aBinding forObject:(id)anObject ++ (CPBinder)getBinding:(CPString)aBinding forObject:(id)anObject { return [[bindingsMap objectForKey:[anObject UID]] objectForKey:aBinding]; } @@ -147,39 +147,10 @@ var CPBindingOperationAnd = 0, var destination = [_info objectForKey:CPObservedObjectKey], keyPath = [_info objectForKey:CPObservedKeyPathKey], options = [_info objectForKey:CPOptionsKey], - newValue = [destination valueForKeyPath:keyPath], - isPlaceholder = CPIsControllerMarker(newValue); - - if (isPlaceholder) - { - switch (newValue) - { - case CPMultipleValuesMarker: - newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values"; - break; - - case CPNoSelectionMarker: - newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || @"No Selection"; - break; - - case CPNotApplicableMarker: - if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption]) - [CPException raise:CPGenericException reason:@"can't transform non applicable key on: "+_source+" value: "+newValue]; - - newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable"; - break; - } - } - else - { - // Only transform the value if the current value is not a placeholder - newValue = [self transformValue:newValue withOptions:options]; - } + newValue = [destination valueForKeyPath:keyPath]; + newValue = [self transformValue:newValue withOptions:options]; [_source setValue:newValue forKey:aBinding]; - - if (aBinding === @"objectValue" && [_source respondsToSelector:@selector(_setCurrentValueIsPlaceholder:)]) - [_source _setCurrentValueIsPlaceholder:isPlaceholder]; } - (void)reverseSetValueFor:(CPString)aBinding @@ -259,7 +230,13 @@ var CPBindingOperationAnd = 0, + (void)exposeBinding:(CPString)aBinding { - [CPKeyValueBinding exposeBinding:aBinding forClass:[self class]]; + [CPBinder exposeBinding:aBinding forClass:[self class]]; +} + + ++ (Class)_binderClassForBinding:(CPString)theBinding +{ + return [CPBinder class]; } - (CPArray)exposedBindings @@ -269,7 +246,7 @@ var CPBindingOperationAnd = 0, while (theClass) { - var temp = [CPKeyValueBinding exposedBindingsForClass:theClass]; + var temp = [CPBinder exposedBindingsForClass:theClass]; if (temp) [exposedBindings addObjectsFromArray:temp]; @@ -293,18 +270,21 @@ var CPBindingOperationAnd = 0, //if (![[self exposedBindings] containsObject:aBinding]) // CPLog.warn("No binding exposed on "+self+" for "+aBinding); + var binderClass = [[self class] _binderClassForBinding:aBinding]; + [self unbind:aBinding]; - [[CPKeyValueBinding alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self]; + [[binderClass alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self]; } - (CPDictionary)infoForBinding:(CPString)aBinding { - return [CPKeyValueBinding infoForBinding:aBinding forObject:self]; + return [CPBinder infoForBinding:aBinding forObject:self]; } - (void)unbind:(CPString)aBinding { - [CPKeyValueBinding unbind:aBinding forObject:self]; + var binderClass = [[self class] _binderClassForBinding:aBinding]; + [binderClass unbind:aBinding forObject:self]; } - (id)_replacementKeyPathForBinding:(CPString)binding @@ -314,7 +294,29 @@ var CPBindingOperationAnd = 0, @end -@implementation _CPKeyValueOrBinding : CPKeyValueBinding +/*! + @ignore + Provides stub implementations that simply call super for the "objectValue" binding + This class should not be necessary but assures backwards compliance with our old way of doing bindings + Every class with a value binding should implement a subclass to handle it's specific value binding logic +*/ +@implementation _CPValueBinder : CPBinder +{ +} + +- (void)setValueFor:(CPString)theBinding +{ + [super setValueFor:@"objectValue"]; +} + +- (void)reverseSetValueFor:(CPString)theBinding +{ + [super reverseSetValueFor:@"objectValue"]; +} + +@end + +@implementation _CPKeyValueOrBinding : CPBinder { } @@ -335,7 +337,7 @@ var CPBindingOperationAnd = 0, @end -@implementation _CPKeyValueAndBinding : CPKeyValueBinding +@implementation _CPKeyValueAndBinding : CPBinder { } diff --git a/AppKit/CPObjectController.j b/AppKit/CPObjectController.j index f08d2d533..6bd261457 100644 --- a/AppKit/CPObjectController.j +++ b/AppKit/CPObjectController.j @@ -137,7 +137,8 @@ { [self setContent:anObject]; - [[CPKeyValueBinding getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"]; + var binderClass = [[self class] _binderClassForBinding:@"contentObject"]; + [[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"]; } - (void)removeObject:(id)anObject @@ -145,7 +146,8 @@ if ([self content] === anObject) [self setContent:nil]; - [[CPKeyValueBinding getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"]; + var binderClass = [[self class] _binderClassForBinding:@"contentObject"]; + [[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"]; } - (void)add:(id)aSender diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j index 975f28f7d..ba79f9c3f 100644 --- a/AppKit/CPTableColumn.j +++ b/AppKit/CPTableColumn.j @@ -62,6 +62,11 @@ CPTableColumnUserResizingMask = 1 << 1; BOOL _disableResizingPosting @accessors(property=disableResizingPosting); } ++ (Class)_binderClassForBinding:(CPString)theBinding +{ + return [CPBinder class]; +} + /*! @ignore */ @@ -507,7 +512,7 @@ CPTableColumnUserResizingMask = 1 << 1; - (void)prepareDataView:(CPView)aDataView forRow:(unsigned)aRow { - var bindingsDictionary = [CPKeyValueBinding allBindingsForObject:self], + var bindingsDictionary = [CPBinder allBindingsForObject:self], keys = [bindingsDictionary allKeys]; for (var i = 0, count = [keys count]; i < count; i++) @@ -547,9 +552,7 @@ CPTableColumnUserResizingMask = 1 << 1; } value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]]; - - // console.log(bindingName+" : "+keyPath+" : "+aRow+" : "+[[destination valueForKeyPath:keyPath] objectAtIndex:aRow]); - [aDataView setValue:value forKey:bindingPath]; + [aDataView setValue:value forKey:@"objectValue"]; } } diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index 5fea8224c..4bba5cd53 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -1078,7 +1078,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; [self setNeedsDisplay:YES]; // FIXME: should be setNeedsDisplayInRect:enclosing rect of new (de)selected rows // but currently -drawRect: is not implemented here - [[CPKeyValueBinding getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectedRowIndexes"]; + var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"]; + [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectedRowIndexes"]; [self _noteSelectionDidChange]; } @@ -2053,18 +2054,17 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; if (!(_implementedDelegateMethods & CPTableViewDelegate_tableView_heightOfRow_)) var height = (_rowHeight + _intercellSpacing.height) * _numberOfRows; + else if ([self numberOfRows] === 0) + var height = 0; else { // if this is the fist run we need to populate the cache if ([self numberOfRows] !== _cachedRowHeights.length) [self noteHeightOfRowsWithIndexesChanged:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])]]; - var heightObject = _cachedRowHeights[_cachedRowHeights.length - 1]; - - if (heightObject) + var heightObject = _cachedRowHeights[_cachedRowHeights.length - 1], height = heightObject.heightAboveRow + heightObject.height + _intercellSpacing.height; - else - height = 0; + } @@ -2082,6 +2082,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; [self setNeedsDisplay:YES]; } + /*! Scrolls the receiver vertically in an enclosing CPClipView so the row specified by rowIndex is visible. @@ -2089,7 +2090,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; */ - (void)scrollRowToVisible:(int)rowIndex { - [self scrollRectToVisible:[self rectOfRow:rowIndex]]; + var visible = [self visibleRect], + rowRect = [self rectOfRow:rowIndex]; + + visible.origin.y = rowRect.origin.y; + visible.size.height = rowRect.size.height; + + [self scrollRectToVisible:visible]; } /*! @@ -2099,8 +2106,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5; */ - (void)scrollColumnToVisible:(int)columnIndex { - [self scrollRectToVisible:[self rectOfColumn:columnIndex]]; - /*FIX ME: tableview header isn't rendered until you click the horizontal scroller (or scroll)*/ + var visible = [self visibleRect], + colRect = [self rectOfColumn:columnIndex]; + + visible.origin.x = colRect.origin.x; + visible.size.width = colRect.size.width; + + [self scrollRectToVisible:visible]; + [_headerView scrollRectToVisible:colRect]; } /*! diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index c0abc894c..91cae055a 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -84,7 +84,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); CPColor _textFieldBackgroundColor; id _placeholderString; - id _originalPlaceholderString; BOOL _currentValueIsPlaceholder; id _delegate; @@ -160,6 +159,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); return "textfield"; } ++ (Class)_binderClassForBinding:(CPString)theBinding +{ + if (theBinding === CPValueBinding) + return [_CPTextFieldValueBinder class]; + + return [super _binderClassForBinding:theBinding]; +} + + (id)themeAttributes { return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), [CPNull null]] @@ -733,6 +740,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); /* @ignore + Sets the internal string value without updating the value in the input element */ - (void)_setStringValue:(id)aValue { @@ -792,30 +800,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); return _placeholderString; } -- (void)_setCurrentValueIsPlaceholder:(BOOL)isPlaceholder -{ - if (isPlaceholder) - { - // Save the original placeholder value so we can restore it later - // Only do this if the placeholder is not already overridden because the bindings logic might call this method - // several times and we don't want the bindings placeholder to ever become the original placeholder - if (!_currentValueIsPlaceholder) - _originalPlaceholderString = [self placeholderString]; - - // Set the current string value as the current placeholder and clear the string value - [self setPlaceholderString:[self stringValue]]; - [self setStringValue:@""]; - } - else if (_originalPlaceholderString) - { - // Restore the original placeholder, the actual textfield value is already correct - // because it was set using setValue:forKey: - [self setPlaceholderString:_originalPlaceholderString]; - } - - _currentValueIsPlaceholder = isPlaceholder; -} - /*! For non-bezeled text fields (typically a label), sizeToFit has two behaviors, depending on the line break mode of the receiver. @@ -1270,7 +1254,6 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey", [self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]]; [self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]]; - } return self; @@ -1299,3 +1282,48 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey", @end +@implementation _CPTextFieldValueBinder : CPBinder +{ +} + +- (void)setValueFor:(CPString)theBinding +{ + var destination = [_info objectForKey:CPObservedObjectKey], + keyPath = [_info objectForKey:CPObservedKeyPathKey], + options = [_info objectForKey:CPOptionsKey], + newValue = [destination valueForKeyPath:keyPath], + isPlaceholder = CPIsControllerMarker(newValue); + + if (isPlaceholder) + { + switch (newValue) + { + case CPMultipleValuesMarker: + newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values"; + break; + + case CPNoSelectionMarker: + newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || @"No Selection"; + break; + + case CPNotApplicableMarker: + if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption]) + [CPException raise:CPGenericException + reason:@"can't transform non applicable key on: "+_source+" value: "+newValue]; + + newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable"; + break; + } + + [_source setPlaceholderString:newValue]; + [_source setObjectValue:nil]; + } + else + { + newValue = [self transformValue:newValue withOptions:options]; + [_source setObjectValue:newValue]; + } +} + +@end + diff --git a/AppKit/CPTokenField.j b/AppKit/CPTokenField.j index 3f98b9a77..a76fa5d1e 100755 --- a/AppKit/CPTokenField.j +++ b/AppKit/CPTokenField.j @@ -264,7 +264,8 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting", - (void)_controlTextDidChange { - var theBinding = [CPKeyValueBinding getBinding:CPValueBinding forObject:self]; + var binderClass = [[self class] _binderClassForBinding:CPValueBinding], + theBinding = [binderClass getBinding:CPValueBinding forObject:self]; if (theBinding) [theBinding reverseSetValueFor:@"objectValue"]; diff --git a/Foundation/CPArray/_CPJavaScriptArray.j b/Foundation/CPArray/_CPJavaScriptArray.j index ca555b23b..640a9bdf0 100644 --- a/Foundation/CPArray/_CPJavaScriptArray.j +++ b/Foundation/CPArray/_CPJavaScriptArray.j @@ -2,6 +2,13 @@ @import "CPMutableArray.j" +var indexOf = Array.prototype.indexOf, + join = Array.prototype.join, + pop = Array.prototype.pop, + push = Array.prototype.push, + slice = Array.prototype.slice, + splice = Array.prototype.splice; + @implementation _CPJavaScriptArray : CPMutableArray { } @@ -28,13 +35,13 @@ - (id)initWithArray:(CPArray)anArray { - return anArray.slice(0); + return slice.call(anArray, 0); } - (id)initWithArray:(CPArray)anArray copyItems:(BOOL)shouldCopyItems { if (!shouldCopyItems) - return anArray.slice(0); + return slice.call(anArray, 0); self = [super init]; @@ -76,19 +83,19 @@ if (arguments[index] === nil) break; - return Array.prototype.slice.call(arguments, 2, index); + return slice.call(arguments, 2, index); } - (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount { if (objects.isa === _CPJavaScriptArray) - return objects.slice(0); + return slice.call(objects, 0); var array = [], index = 0; for (; index < aCount; ++index) - array.push([objects objectAtIndex:index]); + push.call(array, [objects objectAtIndex:index]); return array; } @@ -106,11 +113,6 @@ return self[anIndex]; } -- (CPUInteger)indexOfObject:(id)anObject -{ - return [self indexOfObject:anObject inRange:nil]; -} - - (CPUInteger)indexOfObject:(id)anObject inRange:(CPRange)aRange { // Only use isEqual: if our object is a CPObject. @@ -129,15 +131,10 @@ return [self indexOfObjectIdenticalTo:anObject inRange:aRange]; } -- (CPUInteger)indexOfObjectIdenticalTo:(id)anObject -{ - return [self indexOfObjectIdenticalTo:anObject inRange:nil]; -} - - (CPUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange { - if (self.indexOf) - return self.indexOf(anObject); + if (indexOf && !aRange) + return indexOf.call(self, anObject); var index = aRange ? aRange.location : 0, count = aRange ? CPMaxRange(aRange) : self.length; @@ -179,32 +176,32 @@ if (aRange.location < 0 || CPMaxRange(aRange) > self.length) [CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"]; - return self.slice(aRange.location, CPMaxRange(aRange)); + return slice.call(self, aRange.location, CPMaxRange(aRange)); } - (CPString)componentsJoinedByString:(CPString)aString { - return self.join(aString); + return join.call(self, aString); } - (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex { - self.splice(anIndex, 0, anObject); + splice.call(self, anIndex, 0, anObject); } - (void)removeObjectAtIndex:(CPUInteger)anIndex { - self.splice(anIndex, 1); + splice.call(self, anIndex, 1); } - (void)addObject:(id)anObject { - self.push(anObject); + push.call(self, anObject); } - (void)removeLastObject { - self.pop(); + pop.call(self); } - (void)replaceObjectAtIndex:(int)anIndex withObject:(id)anObject @@ -214,7 +211,7 @@ - (void)copy { - return self.slice(0); + return slice.call(self, 0); } - (Class)classForCoder diff --git a/Foundation/CPSet/CPMutableSet.j b/Foundation/CPSet/CPMutableSet.j index ecaa11e6b..d5cdafe76 100644 --- a/Foundation/CPSet/CPMutableSet.j +++ b/Foundation/CPSet/CPMutableSet.j @@ -132,3 +132,5 @@ } @end + +@import "_CPConcreteMutableSet.j" diff --git a/Foundation/CPSet/CPSet.j b/Foundation/CPSet/CPSet.j index 1d185c614..db19b8d56 100644 --- a/Foundation/CPSet/CPSet.j +++ b/Foundation/CPSet/CPSet.j @@ -316,7 +316,7 @@ objectEnumerator = [self objectEnumerator]; while ((object = [objectEnumerator nextObject]) !== nil) - if (![self containsObject:object]) + if (![aSet containsObject:object]) return NO; return YES; @@ -459,4 +459,6 @@ var _CPSharedPlaceholderSet = nil; @end -@import "_CPConcreteMutableSet.j" +// We actually want _CPConcreteMutableSet, but this introduces the possibility of an invalid @import loop. +// This will be correctly solved when we move to true immutable/mutable pairs. +@import "CPMutableSet.j" diff --git a/Tests/AppKit/CPKeyValueBindingTest.j b/Tests/AppKit/CPKeyValueBindingTest.j index 6944103c6..2ee9e5f4d 100644 --- a/Tests/AppKit/CPKeyValueBindingTest.j +++ b/Tests/AppKit/CPKeyValueBindingTest.j @@ -173,11 +173,11 @@ var testView = [DataViewTester new]; [tableColumn prepareDataView:testView forRow:0]; [self assert:'1' equals:testView.lastValue]; - [self assert:'value' equals:testView.lastKey]; + [self assert:'objectValue' equals:testView.lastKey]; [tableColumn prepareDataView:testView forRow:1]; [self assert:'3' equals:testView.lastValue]; - [self assert:'value' equals:testView.lastKey]; + [self assert:'objectValue' equals:testView.lastKey]; // Test that CPTableColumn is optimized to only read one value per row. [self assert:0 equals:[content[2] accesses] message:"row 2 used "+[content[2] accesses]+" accesses but was never prepared"]; @@ -191,7 +191,7 @@ [tableColumn prepareDataView:testView forRow:1]; [self assert:'old' equals:testView.lastValue]; - [self assert:'value' equals:testView.lastKey]; + [self assert:'objectValue' equals:testView.lastKey]; } - (void)testTextField @@ -215,16 +215,33 @@ [textField bind:@"value" toObject:arrayController withKeyPath:@"selection.cheese" options:options]; [self assert:@"yellow" equals:[textField stringValue] message:@"text field string value should be 'yellow'"]; - [self assert:@"cheese" equals:[textField placeholderString] message:@"text field placeholder should be 'cheese'"]; [arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]]; [self assert:@"" equals:[textField stringValue] message:@"text field string value should be cleared"]; [self assert:@"Multiple Values" equals:[textField placeholderString] message:@"text field placeholder should be 'Multiple Values'"]; + [textField unbind:@"value"]; + // Cocoa doesn't do this + // [self assert:@"cheese" equals:[textField placeholderString] message:@"text field placeholder should be reset"]; + + [textField bind:@"value" toObject:arrayController withKeyPath:@"selection.cheese" options:options]; + [arrayController setSelectionIndex:0]; [self assert:@"yellow" equals:[textField stringValue] message:"text field string value should be 'yellow'"]; - [self assert:@"cheese" equals:[textField placeholderString] message:"text field placeholder should be restored"]; + + // Cocoa doesn't do this + // [self assert:@"cheese" equals:[textField placeholderString] message:"text field placeholder should be restored"]; + + textField = [[CPTextField alloc] init]; + [textField bind:@"value" toObject:arrayController withKeyPath:@"selection.cheese" options:options]; + [arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]]; + [self assert:@"Multiple Values" equals:[textField placeholderString] message:@"text field placeholder should 'Multiple Values'"]; + + // Cocoa doesn't do this + // [arrayController setSelectionIndex:0]; + // [self assert:@"" equals:[textField placeholderString] message:@"empty text field placeholder should be restored"]; + } - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext diff --git a/Tests/Foundation/CPSetTest.j b/Tests/Foundation/CPSetTest.j index f609cb336..fda5a1349 100644 --- a/Tests/Foundation/CPSetTest.j +++ b/Tests/Foundation/CPSetTest.j @@ -196,6 +196,14 @@ [self assertFalse:[set containsObject:nil]]; } +- (void)testIsSubsetOfSet +{ + var set = [CPSet setWithArray:[1, 2, 3, 4, 5]]; + [self assertTrue:[[CPSet setWithArray:[1, 2, 3]] isSubsetOfSet:set]]; + [self assertFalse:[[CPSet setWithArray:[1, 2, 3, 100]] isSubsetOfSet:set]]; + [self assertTrue:[[CPSet new] isSubsetOfSet:set]]; +} + - (void)testDescription { var set = [CPSet new];