NEW: bind:toObject: ... now checks if another binding mutually exclusive is already binded. Throws an exception if this is the case.

Controls can implement -isExlusiveBinding: to determine if bindings are exclusive. Defaults to NO.
Exclusive bindings are mainly CPSelectedIndexBinding | CPSelectedTagBinding | CPSelectedValueBinding
This commit is contained in:
cacaodev
2014-09-09 20:31:15 +02:00
parent e051d02ad3
commit 68b193a782
5 changed files with 103 additions and 42 deletions
+29
View File
@@ -173,6 +173,24 @@ var CPBindingOperationAnd = 0,
return self;
}
+ (BOOL)isBindingAllowed:(CPString)aBinding forObject:(id)anObject
{
if ([[anObject class] isBindingExclusive:aBinding])
{
var bindingsForObject = [bindingsMap objectForKey:[anObject UID]],
allBindings = [bindingsForObject allKeys],
count = [allBindings count];
while(count--)
{
if ([[anObject class] isBindingExclusive:allBindings[count]])
return NO;
}
}
return YES;
}
- (void)raiseIfNotApplicable:(id)aValue forKeyPath:(CPString)keyPath options:(CPDictionary)options
{
if (aValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
@@ -401,6 +419,11 @@ var CPBindingOperationAnd = 0,
return [CPBinder class];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return NO;
}
- (CPArray)exposedBindings
{
var exposedBindings = [],
@@ -429,6 +452,12 @@ var CPBindingOperationAnd = 0,
if (!anObject || !aKeyPath)
return CPLog.error("Invalid object or path on " + self + " for " + aBinding);
if (![CPBinder isBindingAllowed:aBinding forObject:self])
{
[CPException raise:CPGenericException
reason:@"Cannot bind " + aBinding + " because a binding with the same functionality is already in use."];
return;
}
//if (![[self exposedBindings] containsObject:aBinding])
// CPLog.warn("No binding exposed on " + self + " for " + aBinding);
+14 -7
View File
@@ -771,13 +771,6 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
[[self selectedItem] setState:CPOffState];
}
- (void)_reverseSetBinding
{
[_CPPopUpButtonSelectionBinder _reverseSetValueFromExclusiveBinderForObject:self];
[super _reverseSetBinding];
}
@end
@implementation CPPopUpButton (BindingSupport)
@@ -800,6 +793,20 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectedIndexBinding ||
aBinding == CPSelectedTagBinding ||
aBinding == CPSelectedValueBinding);
}
- (void)_reverseSetBinding
{
[_CPPopUpButtonSelectionBinder _reverseSetValueFromExclusiveBinderForObject:self];
[super _reverseSetBinding];
}
@end
@implementation _CPPopUpButtonContentBinder : CPBinder
+7
View File
@@ -407,6 +407,13 @@ var CPRadioGroupRadiosKey = @"CPRadioGroupRadiosKey",
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectedIndexBinding ||
aBinding == CPSelectedTagBinding ||
aBinding == CPSelectedValueBinding);
}
@end
@implementation _CPRadioGroupSelectionBinder : CPBinder
+38 -35
View File
@@ -49,7 +49,6 @@ CPSegmentSwitchTrackingMomentary = 2;
unsigned _trackingSegment;
BOOL _trackingHighlighted;
BOOL _hasBinding;
}
+ (CPString)defaultThemeClass
@@ -75,19 +74,6 @@ CPSegmentSwitchTrackingMomentary = 2;
};
}
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if ([self _isSelectionBinding:aBinding])
return [_CPSegmentedControlBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)_isSelectionBinding:(CPString)aBinding
{
return (aBinding === CPSelectedIndexBinding || aBinding === CPSelectedLabelBinding || aBinding === CPSelectedTagBinding);
}
- (id)initWithFrame:(CGRect)aRect
{
_segments = [];
@@ -105,27 +91,6 @@ CPSegmentSwitchTrackingMomentary = 2;
return self;
}
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
{
if ([[self class] _isSelectionBinding:aBinding] && _trackingMode !== CPSegmentSwitchTrackingSelectOne)
{
CPLog.warn("Binding " + aBinding + " needs CPSegmentSwitchTrackingSelectOne tracking mode");
return;
}
if (_hasBinding)
[_CPSegmentedControlBinder unbindAllForObject:self];
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
_hasBinding = YES;
}
- (void)_reverseSetBinding
{
[_CPSegmentedControlBinder _reverseSetValueFromExclusiveBinderForObject:self];
}
/*!
Returns the tag of the selected segment.
*/
@@ -957,6 +922,44 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
@end
@implementation CPSegmentedControl (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if ([self _isSelectionBinding:aBinding])
return [_CPSegmentedControlBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)_isSelectionBinding:(CPString)aBinding
{
return (aBinding === CPSelectedIndexBinding || aBinding === CPSelectedLabelBinding || aBinding === CPSelectedTagBinding);
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return [self _isSelectionBinding:aBinding];
}
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
{
if ([[self class] _isSelectionBinding:aBinding] && _trackingMode !== CPSegmentSwitchTrackingSelectOne)
{
CPLog.warn("Binding " + aBinding + " needs CPSegmentSwitchTrackingSelectOne tracking mode");
return;
}
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
}
- (void)_reverseSetBinding
{
[_CPSegmentedControlBinder _reverseSetValueFromExclusiveBinderForObject:self];
}
@end
var CPSegmentedControlNoSelectionPlaceholder = "CPSegmentedControlNoSelectionPlaceholder";
@implementation _CPSegmentedControlBinder : CPBinder
+15
View File
@@ -342,6 +342,21 @@
[self assert:@"brie" equals:[control2 objectValue] message:@"control2 objectValue is wrong"];
}
- (void)testExclusiveBindingsThrowsException
{
var segmented = [[CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
[segmented setSegmentCount:3];
var model = [BindingTester testerWithCheese:2];
[segmented bind:CPSelectedIndexBinding toObject:model withKeyPath:@"cheese" options:nil];
[self assertThrows:function()
{
[segmented bind:CPSelectedTagBinding toObject:model withKeyPath:@"cheese" options:nil];
}];
}
@end
@implementation TextField : CPTextField