Merge branch 'master' of github.com:280north/cappuccino into eventualbuddha-CPTokenField-sendAction-fix

Conflicts:
	AppKit/CPTokenField.j
This commit is contained in:
Alexander Ljungberg
2011-01-07 18:53:44 -03:00
16 changed files with 263 additions and 123 deletions
+2 -1
View File
@@ -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;
}
+4 -1
View File
@@ -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]];
}
+58
View File
@@ -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
+2 -1
View File
@@ -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];
+9 -7
View File
@@ -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
+42 -40
View File
@@ -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
{
}
+4 -2
View File
@@ -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
+7 -4
View File
@@ -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"];
}
}
+22 -9
View File
@@ -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];
}
/*!
+54 -26
View File
@@ -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
+2 -1
View File
@@ -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"];
+21 -24
View File
@@ -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
+2
View File
@@ -132,3 +132,5 @@
}
@end
@import "_CPConcreteMutableSet.j"
+4 -2
View File
@@ -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"
+22 -5
View File
@@ -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
+8
View File
@@ -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];