From 9946987f6906a38a9916cbb1c692b03c18b26fe7 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 19 Oct 2015 16:07:49 +0200 Subject: [PATCH 01/12] Fixed: Cache ThemeState for better performance --- AppKit/CPButton.j | 2 +- AppKit/CPTableHeaderView.j | 2 +- AppKit/CPTableView.j | 6 --- AppKit/CPTheme.j | 60 +++++++++++++++++++----- AppKit/CPTokenField.j | 6 --- AppKit/Themes/Aristo2/ThemeDescriptors.j | 2 +- AppKit/_CPObject+Theme.j | 55 ++++++++++++++++++++-- Tests/AppKit/CPViewTest.j | 2 +- 8 files changed, 102 insertions(+), 33 deletions(-) diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j index e2b7c1074..c78d160e8 100644 --- a/AppKit/CPButton.j +++ b/AppKit/CPButton.j @@ -280,7 +280,7 @@ CPButtonImageOffset = 3.0; break; case CPOffState: - [self unsetThemeState:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]]; + [self unsetThemeStates:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]]; } } diff --git a/AppKit/CPTableHeaderView.j b/AppKit/CPTableHeaderView.j index 36d6d1ef1..3c4532d2c 100644 --- a/AppKit/CPTableHeaderView.j +++ b/AppKit/CPTableHeaderView.j @@ -622,7 +622,7 @@ var CPTableHeaderViewResizeZone = 3.0, [[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES]; // The underlying column header shows normal state - [headerView unsetThemeState:CPThemeStateHighlighted | CPThemeStateSelected]; + [headerView unsetThemeStates:[CPThemeStateHighlighted, CPThemeStateSelected]]; // Keep track of the location within the column header where the original mousedown occurred _columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag]; diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j index d2f2edb58..0762c2b9d 100644 --- a/AppKit/CPTableView.j +++ b/AppKit/CPTableView.j @@ -6400,18 +6400,12 @@ var CPTableViewDataSourceKey = @"CPTableViewDataSourceKey", - (BOOL)setThemeState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); - [super setThemeState:aState]; [self recursivelyPerformSelector:@selector(setThemeState:) withObject:aState startingFrom:self]; } - (BOOL)unsetThemeState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); - [super unsetThemeState:aState]; [self recursivelyPerformSelector:@selector(unsetThemeState:) withObject:aState startingFrom:self]; } diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 749b8fa4a..6c207ffc7 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -389,28 +389,64 @@ ThemeState.prototype.isSubsetOf = function(aState) ThemeState.prototype.without = function(aState) { - if (!aState || aState === [CPNull null]) - return this; + var firstTransform = CPThemeWithoutTransform[this._stateNameString], + result; - var newStates = {}; - for (var stateName in this._stateNames) - { - if (!this._stateNames.hasOwnProperty(stateName)) - continue; - - if (!aState._stateNames[stateName]) - newStates[stateName] = true; + if (firstTransform) { + result = firstTransform[aState._stateNameString]; + if (result) + return result; } - return ThemeState._cacheThemeState(new ThemeState(newStates)); + if (!aState || aState === [CPNull null]) + result = this; + else + { + var newStates = {}; + for (var stateName in this._stateNames) + { + if (!this._stateNames.hasOwnProperty(stateName)) + continue; + + if (!aState._stateNames[stateName]) + newStates[stateName] = true; + } + + result = ThemeState._cacheThemeState(new ThemeState(newStates)); + } + + if (!firstTransform) + firstTransform = CPThemeWithoutTransform[this._stateNameString] = {}; + + firstTransform[aState._stateNameString] = result; + + return result; } ThemeState.prototype.and = function(aState) { - return CPThemeState(this, aState); + var firstTransform = CPThemeAndTransform[this._stateNameString], + result; + + if (firstTransform) { + result = firstTransform[aState._stateNameString]; + if (result) + return result; + } + + result = CPThemeState(this, aState); + + if (!firstTransform) + firstTransform = CPThemeAndTransform[this._stateNameString] = {}; + + firstTransform[aState._stateNameString] = result; + + return result; } var CPThemeStates = {}; +var CPThemeWithoutTransform = {}; +var CPThemeAndTransform = {}; ThemeState._cacheThemeState = function(aState) { diff --git a/AppKit/CPTokenField.j b/AppKit/CPTokenField.j index 3bc4b3ea3..b5a9f078c 100644 --- a/AppKit/CPTokenField.j +++ b/AppKit/CPTokenField.j @@ -1475,9 +1475,6 @@ CPTokenFieldDeleteButtonType = 1; - (BOOL)setThemeState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); - var r = [super setThemeState:aState]; // Share hover state with the disclosure and delete buttons. @@ -1492,9 +1489,6 @@ CPTokenFieldDeleteButtonType = 1; - (BOOL)unsetThemeState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); - var r = [super unsetThemeState:aState]; // Share hover state with the disclosure and delete button. diff --git a/AppKit/Themes/Aristo2/ThemeDescriptors.j b/AppKit/Themes/Aristo2/ThemeDescriptors.j index aed9a6362..ee6917791 100644 --- a/AppKit/Themes/Aristo2/ThemeDescriptors.j +++ b/AppKit/Themes/Aristo2/ThemeDescriptors.j @@ -326,7 +326,7 @@ var themedButtonValues = nil, var button = [self button]; [button setTitle:@"OK"]; - [button setThemeState:[CPButtonStateBezelStyleRounded, CPThemeStateDefault]]; + [button setThemeStates:[CPButtonStateBezelStyleRounded, CPThemeStateDefault]]; return button; } diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index e63dfffc2..fec7533c9 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -38,8 +38,11 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (unsigned)themeState; - (BOOL)hasThemeState:(ThemeState)aState; +- (BOOL)hasThemeStates:(CPArray)states; - (BOOL)setThemeState:(ThemeState)aState; +- (BOOL)setThemeStates:(CPArray)aState; - (BOOL)unsetThemeState:(ThemeState)aState; +- (BOOL)unsetThemeStates:(CPArray)aState; - (BOOL)hasThemeAttribute:(CPString)aName; - (void)objectDidChangeTheme; @@ -74,29 +77,47 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (BOOL)hasThemeState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - return _themeState.hasThemeState.apply(_themeState, aState); +#if DEBUG + if (aState && aState.isa && [aState isKindOfClass:CPArray]) + [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'hasThemeStates: instead: " + aState]; +#endif return _themeState.hasThemeState(aState); } +- (BOOL)hasThemeStates:(CPArray)states +{ + var i = [states count], + aState = [states objectAtIndex:--i]; + + while (i > 0) + { + aState = aState.and([states objectAtIndex:--i]); + } + return _themeState.hasThemeState(aState); +} + - (BOOL)setThemeState:(ThemeState)aState { +#if DEBUG if (aState && aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); + [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'setThemeStates: instead: " + aState]; +#endif if (_themeState.hasThemeState(aState)) return NO; - _themeState = CPThemeState(_themeState, aState); + _themeState = _themeState.and(aState); return YES; } - (BOOL)unsetThemeState:(ThemeState)aState { +#if DEBUG if (aState && aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); + [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'unsetThemeStates: instead: " + aState]; +#endif var oldThemeState = _themeState; _themeState = _themeState.without(aState); @@ -107,6 +128,30 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", return YES; } +- (BOOL)setThemeStates:(CPArray)states +{ + var i = [states count], + aState = [states objectAtIndex:--i]; + + while (i > 0) + { + aState = aState.and([states objectAtIndex:--i]); + } + return [self setThemeState:aState]; +} + +- (BOOL)unsetThemeStates:(CPArray)states +{ + var i = [states count], + aState = [states objectAtIndex:--i]; + + while (i > 0) + { + aState = aState.and([states objectAtIndex:--i]); + } + return [self unsetThemeState:aState]; +} + #pragma mark Theme Attributes + (CPString)defaultThemeClass diff --git a/Tests/AppKit/CPViewTest.j b/Tests/AppKit/CPViewTest.j index 9c11b4de5..7b2897ade 100644 --- a/Tests/AppKit/CPViewTest.j +++ b/Tests/AppKit/CPViewTest.j @@ -69,7 +69,7 @@ var methodCalled; [self assertTrue:[view hasThemeState:CPThemeStateDisabled] message:@"CPView should be in state CPThemeStateDisabled"]; [self assertTrue:[view hasThemeState:CPThemeStateBordered] message:@"CPView should be in state CPThemeStateBordered"]; [self assertTrue:[view hasThemeState:CPThemeState(CPThemeStateBordered, CPThemeStateDisabled)] message:@"CPView should be in the combined state of CPThemeStateDisabled and CPThemeStateBordered"]; - [self assertTrue:[view hasThemeState:[CPThemeStateBordered, CPThemeStateDisabled]] message:@"hasThemeState works with an array argument"]; + [self assertTrue:[view hasThemeStates:[CPThemeStateBordered, CPThemeStateDisabled]] message:@"hasThemeState works with an array argument"]; [self assertFalse:[view hasThemeState:CPThemeState(CPThemeStateNormal)] message:@"CPView should not be in CPThemeStateNormal"]; } From 5e1f1675b88c58cf7b21d979db402bfc5ef9df39 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 19 Oct 2015 16:18:14 +0200 Subject: [PATCH 02/12] =?UTF-8?q?Fixed:=20Method=20=E2=80=99=5FthemeAttrib?= =?UTF-8?q?utes=E2=80=99=20never=20saved=20anything=20in=20the=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppKit/_CPObject+Theme.j | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index fec7533c9..1e37c1adc 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -186,9 +186,14 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", var theClass = [self class], CPObjectClass = [CPObject class], - attributes = [], + attributes = CachedThemeAttributes[class_getName(theClass)], nullValue = [CPNull null]; + if (attributes) + return attributes; + else + attributes = []; + for (; theClass && theClass !== CPObjectClass; theClass = [theClass superclass]) { var cachedAttributes = CachedThemeAttributes[class_getName(theClass)]; @@ -196,8 +201,6 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", if (cachedAttributes) { attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes; - CachedThemeAttributes[[self className]] = attributes; - break; } @@ -219,6 +222,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", } } + CachedThemeAttributes[[self className]] = attributes; + return attributes; } From 3a91f43bbb8af08b139bb4652bc4770470041062 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Tue, 20 Oct 2015 16:19:04 +0200 Subject: [PATCH 03/12] Fixed: Test cases are working correctly --- AppKit/CPTheme.j | 25 ++++++++++++------------- Tests/AppKit/CPViewTest.j | 10 +++++----- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 6c207ffc7..61b7f8afc 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -389,6 +389,9 @@ ThemeState.prototype.isSubsetOf = function(aState) ThemeState.prototype.without = function(aState) { + if (!aState || aState === [CPNull null]) + return this; + var firstTransform = CPThemeWithoutTransform[this._stateNameString], result; @@ -398,23 +401,19 @@ ThemeState.prototype.without = function(aState) return result; } - if (!aState || aState === [CPNull null]) - result = this; - else + var newStates = {}; + + for (var stateName in this._stateNames) { - var newStates = {}; - for (var stateName in this._stateNames) - { - if (!this._stateNames.hasOwnProperty(stateName)) - continue; + if (!this._stateNames.hasOwnProperty(stateName)) + continue; - if (!aState._stateNames[stateName]) - newStates[stateName] = true; - } - - result = ThemeState._cacheThemeState(new ThemeState(newStates)); + if (!aState._stateNames[stateName]) + newStates[stateName] = true; } + result = ThemeState._cacheThemeState(new ThemeState(newStates)); + if (!firstTransform) firstTransform = CPThemeWithoutTransform[this._stateNameString] = {}; diff --git a/Tests/AppKit/CPViewTest.j b/Tests/AppKit/CPViewTest.j index 7b2897ade..5131a2fc3 100644 --- a/Tests/AppKit/CPViewTest.j +++ b/Tests/AppKit/CPViewTest.j @@ -94,7 +94,7 @@ var methodCalled; [self assert:String(CPThemeState(CPThemeStateDisabled, CPThemeStateHighlighted)) equals:String([view themeState]) message:@"The view should be in the combined state of CPThemeStateDisabled and CPThemeStateHighlighted"]; [view unsetThemeState:[view themeState]]; - [view setThemeState:[CPThemeStateSelected, CPThemeStateDisabled]]; + [view setThemeStates:[CPThemeStateSelected, CPThemeStateDisabled]]; [self assert:String(CPThemeState(CPThemeStateDisabled, CPThemeStateSelected)) equals:String([view themeState]) message:@"setThemeState works with array argument"]; } @@ -117,20 +117,20 @@ var methodCalled; [self assert:String(CPThemeStateNormal) equals:String([view themeState]) message:@"CPView should be able to unset a combined theme state"]; [view setThemeState:CPThemeState(CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateBordered)]; - [view unsetThemeState:[CPThemeStateBordered, CPThemeStateHighlighted]]; + [view unsetThemeStates:[CPThemeStateBordered, CPThemeStateHighlighted]]; [self assert:String(CPThemeStateDisabled) equals:String([view themeState]) message:@"unsetThemeState works with array argument"]; [view setThemeState:CPThemeStateDisabled]; - [view unsetThemeState:[CPThemeStateDisabled, CPThemeStateHighlighted]]; + [view unsetThemeStates:[CPThemeStateDisabled, CPThemeStateHighlighted]]; [self assert:String(CPThemeStateNormal) equals:String([view themeState]) message:@"CPView should be able to unset a combined theme state that has more theme states than the view currently has"]; [view setThemeState:CPThemeState(CPThemeStateDisabled, CPThemeStateBordered)]; - var returnValue = [view unsetThemeState:[CPThemeStateDisabled, CPThemeStateHighlighted]]; + var returnValue = [view unsetThemeStates:[CPThemeStateDisabled, CPThemeStateHighlighted]]; [self assert:String(CPThemeStateBordered) equals:String([view themeState]) message:@"CPView should be able to unset a combined theme state that has not entirely overlapping themestates"]; [self assertTrue:returnValue message:@"When unsetThemeState successfully unsets anything, it return YES"]; [view setThemeState:CPThemeState(CPThemeStateDisabled, CPThemeStateBordered)]; - var returnValue = [view unsetThemeState:[CPThemeStateSelected, CPThemeStateHighlighted]]; + var returnValue = [view unsetThemeStates:[CPThemeStateSelected, CPThemeStateHighlighted]]; [self assert:String(CPThemeState(CPThemeStateDisabled, CPThemeStateBordered)) equals:String([view themeState]) message:@"CPView not unset any theme states it does not have"]; [self assertFalse:returnValue message:@"When unsetThemeState doesn't unset anything, it returns NO"]; From fc411c5c1c441713639c9dda97b6014877bead4f Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Mon, 19 Oct 2015 16:38:53 +0200 Subject: [PATCH 04/12] Fixed: Cache ThemeAttribute Conflicts: AppKit/CPTheme.j --- AppKit/CPButtonBar.j | 12 +- AppKit/CPDatePicker/_CPDatePickerCalendar.j | 32 ++-- AppKit/CPStepper.j | 12 +- AppKit/CPTheme.j | 174 ++++++++++++++++---- AppKit/CPView.j | 15 +- AppKit/Themes/BlendKit/BKThemeDescriptor.j | 20 ++- AppKit/_CPAutocompleteMenu.j | 4 +- AppKit/_CPObject+Theme.j | 96 ++++++++--- 8 files changed, 280 insertions(+), 85 deletions(-) diff --git a/AppKit/CPButtonBar.j b/AppKit/CPButtonBar.j index f27819689..322d8c65a 100644 --- a/AppKit/CPButtonBar.j +++ b/AppKit/CPButtonBar.j @@ -272,15 +272,15 @@ currentButtonOffset += width - 1; } - [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]]; - [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, ]]; - [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]]; + [button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered]]; + [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, ]]; + [button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered]]; [button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered]; // FIXME shouldn't need this - [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; - [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; - [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; + [button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; + [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; + [button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]]; [self addSubview:button]; } diff --git a/AppKit/CPDatePicker/_CPDatePickerCalendar.j b/AppKit/CPDatePicker/_CPDatePickerCalendar.j index c4ecb3866..b3140cab1 100644 --- a/AppKit/CPDatePicker/_CPDatePickerCalendar.j +++ b/AppKit/CPDatePicker/_CPDatePickerCalendar.j @@ -1114,30 +1114,30 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su" [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled]; [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]; [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateHighlighted] forThemeAttribute:@"font" inState:CPThemeStateHighlighted]; [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-color" inState:CPThemeStateHighlighted]; [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateHighlighted]; [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateHighlighted]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]]; - [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]]; + [_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]]; [self addSubview:_textField]; diff --git a/AppKit/CPStepper.j b/AppKit/CPStepper.j index 3436d062f..42442ea04 100644 --- a/AppKit/CPStepper.j +++ b/AppKit/CPStepper.j @@ -188,12 +188,12 @@ [_buttonUp setFrame:upFrame]; [_buttonDown setFrame:downFrame]; - [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered]; - [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]]; - [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]]; - [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered]; - [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]]; - [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]]; + [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered]; + [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]]; + [_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]]; + [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered]; + [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]]; + [_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]]; } - (void)_sizeToFit diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 61b7f8afc..9dc12e1e1 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -557,6 +557,26 @@ CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular"); CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall"); CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); +CPThemeStateNormalString = String(CPThemeStateNormal); + + +CPThemeAttributeCache = {}; // CPThemeAttributeCache[_name][themeDefaultAttribute._UID][_defaultValue] -> CPThemeAttribute + +var stringify = JSON.stringify; + +function CPStringthemeAttributeCacheString(array) +{ + var returnString = "["; + + for (var i = 0, size = array.length; i < size; i++) + { + var aValue = array[i]; + returnString += aValue == null ? aValue : aValue._UID || (aValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aValue) : stringify(aValue)) + } + + return returnString + "]"; +} + @implementation _CPThemeAttribute : CPObject { CPString _name; @@ -564,10 +584,26 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); CPDictionary _values @accessors(readonly, getter=values); JSObject _cache; + JSObject _setValueTransformCache; + JSObject _removeValueTransformCache; _CPThemeAttribute _themeDefaultAttribute; } -- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue ++ (id)attributeWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute +{ + var defaultAttributeCache = CPThemeAttributeCache[aName] || (CPThemeAttributeCache[aName] = {}), + defaultAttributeUID = aDefaultAttribute ? aDefaultAttribute._UID : nil, + defaultValueCache = defaultAttributeCache[defaultAttributeUID] || (defaultAttributeCache[defaultAttributeUID] = {}), + aDefaultValueString = aDefaultValue == null ? aDefaultValue : aDefaultValue._UID || (aDefaultValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aDefaultValue) : JSON.stringify(aDefaultValue)), + attribute = defaultValueCache[aDefaultValueString]; + + if (!attribute) + attribute = defaultValueCache[aDefaultValueString] = [[_CPThemeAttribute alloc] initWithName:aName defaultValue:aDefaultValue defaultAttribute:aDefaultAttribute]; + + return attribute; +} + +- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute { self = [super init]; @@ -577,6 +613,8 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); _name = aName; _defaultValue = aDefaultValue; _values = @{}; + if (aDefaultAttribute) + _themeDefaultAttribute = aDefaultAttribute; } return self; @@ -597,7 +635,64 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); return [_values count] > 0; } -- (void)setValue:(id)aValue +- (_CPThemeAttribute)attributeBySettingValue:(id)aValue +{ + var transformStateDict = self._setValueTransformCacheN || (self._setValueTransformCacheN = {}), + transformValueDict = transformStateDict[CPThemeStateNormalString] || (transformStateDict[CPThemeStateNormalString] = {}), + aValueString = aValue == null ? aValue : aValue._UID || (aValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aValue) : stringify(aValue)), + attribute = transformValueDict[aValueString]; + + if (!attribute) + { + attribute = transformValueDict[aValueString] = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; + if (aValue !== undefined && aValue !== nil) + attribute._values = @{ CPThemeStateNormalString: aValue }; + } + + return attribute; +} + +- (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState +{ + var shouldRemoveValue = aValue === undefined || aValue === nil, + transformStateDict = shouldRemoveValue ? self._removeValueTransformCache || (self._removeValueTransformCache = {}) : self._setValueTransformCache || (self._setValueTransformCache = {}), + stateString = String(aState), + transformValueDict = transformStateDict[stateString] || (transformStateDict[stateString] = {}), + aValueString = aValue == null ? aValue : aValue._UID || (aValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aValue) : stringify(aValue)), + attribute = transformValueDict[aValueString]; + + if (!attribute) + { + attribute = transformValueDict[aValueString] = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; + + var values = [_values copy]; +// var values = _values; + +/* if (values != null) + { + values = [values copy]; +*/ if (shouldRemoveValue) + [values removeObjectForKey:stateString]; + else + [values setObject:aValue forKey:stateString]; + attribute._values = values; +/* } + else + { + if (!shouldRemoveValue) + { + values = [[CPDictionary alloc] init]; + [values setObject:aValue forKey:stateString]; +// values = @{stateString: aValue}; + attribute._values = values; + } + } + */ } + + return attribute; +} + +/*- (void)setValue:(id)aValue { _cache = {}; @@ -615,7 +710,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); [_values removeObjectForKey:String(aState)]; else [_values setObject:aValue forKey:String(aState)]; -} +}*/ - (id)value { @@ -639,7 +734,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); if (aState._stateNameCount > 1) { var states = [_values allKeys], - count = states.length, + count = states ? states.length : 0, largestThemeState = 0; while (count--) @@ -674,52 +769,76 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); _cache[stateName] = value; - return value; +/* var now = [CPDate new]; + var elapsedSeconds = [now timeIntervalSinceReferenceDate] - [start timeIntervalSinceReferenceDate]; + + CPLog.trace(@"valueForState:" + aState + " in " + elapsedSeconds + @" seconds"); + +*/ return value; } -- (void)setParentAttribute:(_CPThemeAttribute)anAttribute +/*- (void)setParentAttribute:(_CPThemeAttribute)anAttribute { if (_themeDefaultAttribute === anAttribute) return; _cache = { }; _themeDefaultAttribute = anAttribute; -} +}*/ - (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute { - var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue]; + var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; mergedAttribute._values = [_values copy]; - [mergedAttribute._values addEntriesFromDictionary:anAttribute._values]; + if (anAttribute._values) + mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy]; return mergedAttribute; } @end + +// This is used to pass 'parrentAttribute' to the coder +var ParentAttributeForCoder = nil; + @implementation _CPThemeAttribute (CPCoding) - (id)initWithCoder:(CPCoder)aCoder { - self = [super init]; + var aName = [aCoder decodeObjectForKey:@"name"], + aDefaultValue = [aCoder decodeObjectForKey:@"defaultValue"], + defaultAttributeCache = CPThemeAttributeCache[aName] || (CPThemeAttributeCache[aName] = {}), + defaultAttributeUID = ParentAttributeForCoder ? ParentAttributeForCoder._UID : nil, // Uses global variable as + defaultValueCache = defaultAttributeCache[defaultAttributeUID] || (defaultAttributeCache[defaultAttributeUID] = {}), + aDefaultValueString = aDefaultValue == null ? aDefaultValue : aDefaultValue._UID || (aDefaultValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aDefaultValue) : stringify(aDefaultValue)), + attribute = defaultValueCache[aDefaultValueString]; + + if (attribute) + self = attribute; + else + self = [super init]; if (self) { - _cache = {}; + defaultValueCache[aDefaultValueString] = self; - _name = [aCoder decodeObjectForKey:@"name"]; - _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"]; + _cache = {}; + _name = aName; _values = @{}; + _defaultValue = aDefaultValue; if ([aCoder containsValueForKey:@"value"]) { - var state = String(CPThemeStateNormal); + var state; if ([aCoder containsValueForKey:@"state"]) state = [aCoder decodeObjectForKey:@"state"]; + else + state = CPThemeStateNormalString; - [_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state]; + self = [self attributeBySettingValue:[aCoder decodeObjectForKey:"value"] forState:state]; } else { @@ -731,7 +850,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); { var key = keys[count]; - [_values setObject:[encodedValues objectForKey:key] forKey:key]; + self = [self attributeBySettingValue:[encodedValues objectForKey:key] forState:key]; } } } @@ -745,7 +864,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); [aCoder encodeObject:_defaultValue forKey:@"defaultValue"]; var keys = [_values allKeys], - count = keys.length; + count = keys ? keys.length : 0; if (count === 1) { @@ -783,7 +902,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute) { var state = [values allKeys][0]; - if (state === String(CPThemeStateNormal)) + if (state === CPThemeStateNormalString) { [aCoder encodeObject:[values objectForKey:state] forKey:key]; @@ -803,28 +922,27 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute) function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass) { - var key = "$a" + anAttributeName; + var key = "$a" + anAttributeName, + parentAttribute = (aTheme && aClass) ? [aTheme attributeWithName:anAttributeName forClass:aClass] : nil; if (![aCoder containsValueForKey:key]) - var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue]; - + var attribute = [_CPThemeAttribute attributeWithName:anAttributeName defaultValue:aDefaultValue defaultAttribute:parentAttribute]; else { + ParentAttributeForCoder = parentAttribute; + var attribute = [aCoder decodeObjectForKey:key]; + ParentAttributeForCoder = nil; + if (!attribute || !attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]]) { - var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue]; + var themeAttribute = [_CPThemeAttribute attributeWithName:anAttributeName defaultValue:aDefaultValue defaultAttribute:parentAttribute]; - [themeAttribute setValue:attribute]; - - attribute = themeAttribute; + attribute = [themeAttribute attributeBySettingValue:attribute]; } } - if (aTheme && aClass) - [attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]]; - return attribute; } diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 0057c0b89..3041765a7 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -3386,8 +3386,19 @@ setBoundsOrigin: [self unsetThemeState:CPThemeStateAppearanceVibrantDark]; } - [_subviews makeObjectsPerformSelector:@selector(_recomputeAppearance)]; -} +// var start = [CPDate new]; + + for (var i = 0, size = [_subviews count]; i < size; i++) + { + [[_subviews objectAtIndex:i] _recomputeAppearance]; + } +// [_subviews makeObjectsPerformSelector:@selector(_recomputeAppearance)]; + +/* var now = [CPDate new]; + var elapsedSeconds = [now timeIntervalSinceReferenceDate] - [start timeIntervalSinceReferenceDate]; + + CPLog.trace(@"_recomputeAppearance " + [_subviews count] + " subviews in " + elapsedSeconds + @" seconds"); +*/} @end diff --git a/AppKit/Themes/BlendKit/BKThemeDescriptor.j b/AppKit/Themes/BlendKit/BKThemeDescriptor.j index 801c6e6ae..188d24e46 100644 --- a/AppKit/Themes/BlendKit/BKThemeDescriptor.j +++ b/AppKit/Themes/BlendKit/BKThemeDescriptor.j @@ -231,10 +231,14 @@ var ItemSizes = { }, value = attributeValueState[1], state = attributeValueState[2]; - if (state) - [aView setValue:value forThemeAttribute:attribute inState:state]; - else + if (state) { + if (state.isa && [state isKindOfClass:CPArray]) + [aView setValue:value forThemeAttribute:attribute inStates:state]; + else + [aView setValue:value forThemeAttribute:attribute inState:state]; + } else { [aView setValue:value forThemeAttribute:attribute]; + } } } @@ -303,10 +307,14 @@ var ItemSizes = { }, } } - if (state) - [aView setValue:value forThemeAttribute:attribute inState:state]; - else + if (state) { + if (state.isa && [state isKindOfClass:CPArray]) + [aView setValue:value forThemeAttribute:attribute inStates:state]; + else + [aView setValue:value forThemeAttribute:attribute inState:state]; + } else { [aView setValue:value forThemeAttribute:attribute]; + } } } } diff --git a/AppKit/_CPAutocompleteMenu.j b/AppKit/_CPAutocompleteMenu.j index cf25c025d..f1b3ecb41 100644 --- a/AppKit/_CPAutocompleteMenu.j +++ b/AppKit/_CPAutocompleteMenu.j @@ -171,9 +171,9 @@ var _CPAutocompleteMenuMaximumHeight = 307; var dataView = [tableColumn dataView], fontNormal = [dataView valueForThemeAttribute:@"font" inState:CPThemeStateTableDataView], - fontSelected = [dataView valueForThemeAttribute:@"font" inState:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]], + fontSelected = [dataView valueForThemeAttribute:@"font" inStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]], contentInsetNormal = [dataView valueForThemeAttribute:@"content-inset" inState:CPThemeStateTableDataView], - contentInsetSelected = [dataView valueForThemeAttribute:@"content-inset" inState:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]]; + contentInsetSelected = [dataView valueForThemeAttribute:@"content-inset" inStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]]; var mergedString = contentArray.join("\n"); diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index 1e37c1adc..e7f848e72 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -244,9 +244,7 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", while (count--) { var attributeName = attributes[count--], - attribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributes[count]]; - - [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]]; + attribute = [_CPThemeAttribute attributeWithName:attributeName defaultValue:attributes[count] defaultAttribute:[theme attributeWithName:attributeName forClass:themeClass]]; _themeAttributes[attributeName] = attribute; } @@ -303,48 +301,104 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); +#if DEBUG + if (aState.isa && [aState isKindOfClass:CPArray]) + [CPException raise:CPInvalidArgumentException reason:self + @": aState can't be an array. Please use 'setValue:forThemeAttribute:inStates:' instead: " + aState]; +#endif - if (!_themeAttributes || !_themeAttributes[aName]) + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - [_themeAttributes[aName] setValue:aValue forState:aState]; + var a = [themeAttr attributeBySettingValue:aValue forState:aState]; + //print("setValue:forThemeAttribute:inState:" + aState + ": " + aName + ": " + aValue + "oldAttribute: " + [themeAttr._values description]); + _themeAttributes[aName] = a; +} + +- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inStates:(CPArray)states +{ + var i = [states count], + aState = [states objectAtIndex:--i]; + + while (i > 0) + { + aState = aState.and([states objectAtIndex:--i]); + } + + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) + [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; + + var a = [themeAttr attributeBySettingValue:aValue forState:aState]; + //print("setValue:forThemeAttribute:inStates:" + aState + ": " + aName + ": " + aValue + "oldAttribute: " + [themeAttr._values description]); + _themeAttributes[aName] = a; } - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName { - if (!_themeAttributes || !_themeAttributes[aName]) + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - [_themeAttributes[aName] setValue:aValue]; + var a = [themeAttr attributeBySettingValue:aValue]; + //print("setValue:forThemeAttribute:" + ": " + aName + ": " + aValue + "oldAttribute: " + [themeAttr._values description]); + _themeAttributes[aName] = a; } - (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState { +#if DEBUG if (aState.isa && [aState isKindOfClass:CPArray]) - aState = CPThemeState.apply(null, aState); + [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'valueForThemeAttribute:inStates:' instead: " + aState]; +#endif - if (!_themeAttributes || !_themeAttributes[aName]) + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - return [_themeAttributes[aName] valueForState:aState]; + return [themeAttr valueForState:aState]; +} + +- (id)valueForThemeAttribute:(CPString)aName inStates:(CPArray)states +{ + var i = [states count], + aState = [states objectAtIndex:--i]; + + while (i > 0) + { + aState = aState.and([states objectAtIndex:--i]); + } + + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) + [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; + + return [themeAttr valueForState:aState]; } - (id)valueForThemeAttribute:(CPString)aName { - if (!_themeAttributes || !_themeAttributes[aName]) + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - return [_themeAttributes[aName] value]; + return [themeAttr value]; } - (id)currentValueForThemeAttribute:(CPString)aName { - if (!_themeAttributes || !_themeAttributes[aName]) + var themeAttr = _themeAttributes && _themeAttributes[aName]; + + if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - return [_themeAttributes[aName] valueForState:_themeState]; + return [themeAttr valueForState:_themeState]; } - (BOOL)hasThemeAttribute:(CPString)aName @@ -369,10 +423,14 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", value = attributeValueState[1], state = attributeValueState[2]; - if (state) - [self setValue:value forThemeAttribute:attribute inState:state]; - else + if (state) { + if (state.isa && [state isKindOfClass:CPArray]) + [self setValue:value forThemeAttribute:attribute inStates:state]; + else + [self setValue:value forThemeAttribute:attribute inState:state]; + } else { [self setValue:value forThemeAttribute:attribute]; + } } } From 83976854308e108c0272b99fd4f1592d4c2c1159 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Thu, 22 Oct 2015 19:50:55 +0200 Subject: [PATCH 05/12] Fixed: Joined caches for theme attributes for better performance Conflicts: AppKit/CPTheme.j --- AppKit/CPTheme.j | 215 +++++++++++++++++---------------------- AppKit/_CPObject+Theme.j | 48 +++++---- 2 files changed, 116 insertions(+), 147 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 9dc12e1e1..8714833b7 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -37,6 +37,46 @@ var CPThemesByName = { }, @ingroup appkit */ +CPThemeAttributeAlloc = { + CPThemeAttributeAlloc0: 0, + CPThemeAttributeAlloc0l: 0, + CPThemeAttributeAlloc0c: 0, + CPThemeAttributeAlloc1c: 0, + CPThemeAttributeAlloc0d: 0, + CPThemeAttributeAlloc0dd: 0, + CPThemeAttributeAlloc1: 0, + CPThemeAttributeAlloc2: 0, + CPThemeAttributeAlloc3: 0, + CPThemeAttributeAlloc4: 0, + CPThemeAttributeAlloc5: 0, + CPThemeAttributeAlloc6: 0, + CPThemeAttributeAlloc7: 0, + CPThemeAttributeAlloc8: 0, + CPThemeAttributeAlloc9: 0, + CPThemeAttributeAllocM: 0, + CPThemeAttributeAllocc0: 0, + CPThemeAttributeAllocc1: 0, + CPThemeAttributeAllocc2: 0, + CPThemeAttributeAllocc3: 0, + CPThemeAttributeAllocc4: 0, + CPThemeAttributeAllocc5: 0, + CPThemeAttributeAllocc6: 0, + CPThemeAttributeAllocc7: 0, + CPThemeAttributeAllocc8: 0, + CPThemeAttributeAllocc9: 0, + CPThemeAttributeAllocc10: 0, + CPThemeAttributeAllocc11: 0, + CPThemeAttributeAllocc12: 0, + CPThemeAttributeAllocc13: 0, + CPThemeAttributeAllocc14: 0, + CPThemeAttributeAllocc15: 0, + CPThemeAttributeAllocc16: 0, + CPThemeAttributeAllocc17: 0, + CPThemeAttributeAllocc18: 0, + CPThemeAttributeAllocc19: 0, + CPThemeAttributeAllocc20: 0, +} + @implementation CPTheme : CPObject { CPString _name; @@ -562,21 +602,6 @@ CPThemeStateNormalString = String(CPThemeStateNormal); CPThemeAttributeCache = {}; // CPThemeAttributeCache[_name][themeDefaultAttribute._UID][_defaultValue] -> CPThemeAttribute -var stringify = JSON.stringify; - -function CPStringthemeAttributeCacheString(array) -{ - var returnString = "["; - - for (var i = 0, size = array.length; i < size; i++) - { - var aValue = array[i]; - returnString += aValue == null ? aValue : aValue._UID || (aValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aValue) : stringify(aValue)) - } - - return returnString + "]"; -} - @implementation _CPThemeAttribute : CPObject { CPString _name; @@ -584,25 +609,9 @@ function CPStringthemeAttributeCacheString(array) CPDictionary _values @accessors(readonly, getter=values); JSObject _cache; - JSObject _setValueTransformCache; - JSObject _removeValueTransformCache; _CPThemeAttribute _themeDefaultAttribute; } -+ (id)attributeWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute -{ - var defaultAttributeCache = CPThemeAttributeCache[aName] || (CPThemeAttributeCache[aName] = {}), - defaultAttributeUID = aDefaultAttribute ? aDefaultAttribute._UID : nil, - defaultValueCache = defaultAttributeCache[defaultAttributeUID] || (defaultAttributeCache[defaultAttributeUID] = {}), - aDefaultValueString = aDefaultValue == null ? aDefaultValue : aDefaultValue._UID || (aDefaultValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aDefaultValue) : JSON.stringify(aDefaultValue)), - attribute = defaultValueCache[aDefaultValueString]; - - if (!attribute) - attribute = defaultValueCache[aDefaultValueString] = [[_CPThemeAttribute alloc] initWithName:aName defaultValue:aDefaultValue defaultAttribute:aDefaultAttribute]; - - return attribute; -} - - (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute { self = [super init]; @@ -612,7 +621,6 @@ function CPStringthemeAttributeCacheString(array) _cache = { }; _name = aName; _defaultValue = aDefaultValue; - _values = @{}; if (aDefaultAttribute) _themeDefaultAttribute = aDefaultAttribute; } @@ -637,16 +645,10 @@ function CPStringthemeAttributeCacheString(array) - (_CPThemeAttribute)attributeBySettingValue:(id)aValue { - var transformStateDict = self._setValueTransformCacheN || (self._setValueTransformCacheN = {}), - transformValueDict = transformStateDict[CPThemeStateNormalString] || (transformStateDict[CPThemeStateNormalString] = {}), - aValueString = aValue == null ? aValue : aValue._UID || (aValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aValue) : stringify(aValue)), - attribute = transformValueDict[aValueString]; + var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; - if (!attribute) - { - attribute = transformValueDict[aValueString] = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; - if (aValue !== undefined && aValue !== nil) - attribute._values = @{ CPThemeStateNormalString: aValue }; + if (aValue !== undefined && aValue !== nil) { + attribute._values = @{ CPThemeStateNormalString: aValue }; } return attribute; @@ -654,64 +656,36 @@ function CPStringthemeAttributeCacheString(array) - (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState { - var shouldRemoveValue = aValue === undefined || aValue === nil, - transformStateDict = shouldRemoveValue ? self._removeValueTransformCache || (self._removeValueTransformCache = {}) : self._setValueTransformCache || (self._setValueTransformCache = {}), - stateString = String(aState), - transformValueDict = transformStateDict[stateString] || (transformStateDict[stateString] = {}), - aValueString = aValue == null ? aValue : aValue._UID || (aValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aValue) : stringify(aValue)), - attribute = transformValueDict[aValueString]; + var shouldRemoveValue = aValue === undefined || aValue === nil; - if (!attribute) - { - attribute = transformValueDict[aValueString] = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; + var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; - var values = [_values copy]; -// var values = _values; +// var values = [_values copy]; + var values = _values; -/* if (values != null) + if (values != null) { values = [values copy]; -*/ if (shouldRemoveValue) - [values removeObjectForKey:stateString]; + if (shouldRemoveValue) + [values removeObjectForKey:String(aState)]; else - [values setObject:aValue forKey:stateString]; + [values setObject:aValue forKey:String(aState)]; attribute._values = values; -/* } + } else { if (!shouldRemoveValue) { values = [[CPDictionary alloc] init]; - [values setObject:aValue forKey:stateString]; + [values setObject:aValue forKey:String(aState)]; // values = @{stateString: aValue}; attribute._values = values; } } - */ } return attribute; } -/*- (void)setValue:(id)aValue -{ - _cache = {}; - - if (aValue === undefined || aValue === nil) - _values = @{}; - else - _values = @{ String(CPThemeStateNormal): aValue }; -} - -- (void)setValue:(id)aValue forState:(ThemeState)aState -{ - _cache = { }; - - if ((aValue === undefined) || (aValue === nil)) - [_values removeObjectForKey:String(aState)]; - else - [_values setObject:aValue forKey:String(aState)]; -}*/ - - (id)value { return [self valueForState:CPThemeStateNormal]; @@ -769,22 +743,20 @@ function CPStringthemeAttributeCacheString(array) _cache[stateName] = value; -/* var now = [CPDate new]; - var elapsedSeconds = [now timeIntervalSinceReferenceDate] - [start timeIntervalSinceReferenceDate]; - - CPLog.trace(@"valueForState:" + aState + " in " + elapsedSeconds + @" seconds"); - -*/ return value; + return value; } -/*- (void)setParentAttribute:(_CPThemeAttribute)anAttribute +- (_CPThemeAttribute)attributeBySettingParentAttribute:(_CPThemeAttribute)anAttribute { if (_themeDefaultAttribute === anAttribute) - return; + return self; - _cache = { }; - _themeDefaultAttribute = anAttribute; -}*/ + var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:anAttribute]; + + attribute._values = [_values copy]; + + return attribute; +} - (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute { @@ -794,9 +766,17 @@ function CPStringthemeAttributeCacheString(array) if (anAttribute._values) mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy]; + var size = [mergedAttribute._values count]; + CPThemeAttributeAlloc[@"CPThemeAttributeAlloc" + (size < 10 ? size : @"M")]++; + return mergedAttribute; } +- (CPString)description +{ + return [super description] + @" Name: " + _name + @", defaultAttribute: " + _themeDefaultAttribute + @", defaultValue: " + _defaultValue + @", values: " + _values; +} + @end @@ -807,27 +787,16 @@ var ParentAttributeForCoder = nil; - (id)initWithCoder:(CPCoder)aCoder { - var aName = [aCoder decodeObjectForKey:@"name"], - aDefaultValue = [aCoder decodeObjectForKey:@"defaultValue"], - defaultAttributeCache = CPThemeAttributeCache[aName] || (CPThemeAttributeCache[aName] = {}), - defaultAttributeUID = ParentAttributeForCoder ? ParentAttributeForCoder._UID : nil, // Uses global variable as - defaultValueCache = defaultAttributeCache[defaultAttributeUID] || (defaultAttributeCache[defaultAttributeUID] = {}), - aDefaultValueString = aDefaultValue == null ? aDefaultValue : aDefaultValue._UID || (aDefaultValue.isa === _CPJavaScriptArray ? CPStringthemeAttributeCacheString(aDefaultValue) : stringify(aDefaultValue)), - attribute = defaultValueCache[aDefaultValueString]; - - if (attribute) - self = attribute; - else - self = [super init]; + self = [super init]; if (self) { - defaultValueCache[aDefaultValueString] = self; - _cache = {}; - _name = aName; - _values = @{}; - _defaultValue = aDefaultValue; + + _name = [aCoder decodeObjectForKey:@"name"]; + _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"], + _values = @{}, + _themeDefaultAttribute = ParentAttributeForCoder; if ([aCoder containsValueForKey:@"value"]) { @@ -836,9 +805,11 @@ var ParentAttributeForCoder = nil; if ([aCoder containsValueForKey:@"state"]) state = [aCoder decodeObjectForKey:@"state"]; else - state = CPThemeStateNormalString; + state = CPThemeStateNormalString - self = [self attributeBySettingValue:[aCoder decodeObjectForKey:"value"] forState:state]; + [_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state]; + + CPThemeAttributeAlloc[@"CPThemeAttributeAlloc1c"]++; } else { @@ -846,11 +817,13 @@ var ParentAttributeForCoder = nil; keys = [encodedValues allKeys], count = keys.length; + CPThemeAttributeAlloc[@"CPThemeAttributeAllocc" + (count < 20 ? count : @"M")]++; + while (count--) { var key = keys[count]; - self = [self attributeBySettingValue:[encodedValues objectForKey:key] forState:key]; + [_values setObject:[encodedValues objectForKey:key] forKey:key]; } } } @@ -920,27 +893,25 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute) return NO; } -function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass) +function CPThemeAttributeDecode(aCoder, attribute) { - var key = "$a" + anAttributeName, - parentAttribute = (aTheme && aClass) ? [aTheme attributeWithName:anAttributeName forClass:aClass] : nil; + var key = "$a" + attribute._name; - if (![aCoder containsValueForKey:key]) - var attribute = [_CPThemeAttribute attributeWithName:anAttributeName defaultValue:aDefaultValue defaultAttribute:parentAttribute]; - else + if ([aCoder containsValueForKey:key]) { - ParentAttributeForCoder = parentAttribute; + ParentAttributeForCoder = attribute._themeDefaultAttribute; - var attribute = [aCoder decodeObjectForKey:key]; + var decodedAttribute = [aCoder decodeObjectForKey:key]; ParentAttributeForCoder = nil; - if (!attribute || !attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]]) + if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]]) { - var themeAttribute = [_CPThemeAttribute attributeWithName:anAttributeName defaultValue:aDefaultValue defaultAttribute:parentAttribute]; + attribute = [attribute attributeBySettingValue:decodedAttribute]; - attribute = [themeAttribute attributeBySettingValue:attribute]; - } + CPThemeAttributeAlloc[@"CPThemeAttributeAlloc0dd"]++; + } else + attribute = decodedAttribute; } return attribute; diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index e7f848e72..1d0359cb9 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -23,8 +23,6 @@ @import "CPTheme.j" -var CachedThemeAttributes = nil; - var CPViewThemeClassKey = @"CPViewThemeClassKey", CPViewThemeStateKey = @"CPViewThemeStateKey"; @@ -179,24 +177,26 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", [self _loadThemeAttributes]; } -+ (CPArray)_themeAttributes -{ - if (!CachedThemeAttributes) - CachedThemeAttributes = {}; +var NULL_THEME = {}; ++ (CPArray)_themeAttributesForTheme:(CPTheme)theme andThemeClass:(CPString)themeClassName +{ var theClass = [self class], - CPObjectClass = [CPObject class], - attributes = CachedThemeAttributes[class_getName(theClass)], - nullValue = [CPNull null]; + theClassName = class_getName(theClass), + themeClassNameCache = theme != nil ? theme._cachedThemeAttributes || (theme._cachedThemeAttributes = {}) : NULL_THEME, + themedCacheAttributes = themeClassNameCache[themeClassName] || (themeClassNameCache[themeClassName] = {}), + attributes = themedCacheAttributes[theClassName]; if (attributes) return attributes; else attributes = []; + var CPObjectClass = [CPObject class]; + for (; theClass && theClass !== CPObjectClass; theClass = [theClass superclass]) { - var cachedAttributes = CachedThemeAttributes[class_getName(theClass)]; + var cachedAttributes = themedCacheAttributes[class_getName(theClass)]; if (cachedAttributes) { @@ -215,14 +215,14 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", while (attributeCount--) { var attributeName = attributeKeys[attributeCount], - attributeValue = [attributeDictionary objectForKey:attributeName]; + attributeValue = [attributeDictionary objectForKey:attributeName], + themeAttribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributeValue defaultAttribute:[theme attributeWithName:attributeName forClass:themeClassName]]; - attributes.push(attributeValue === nullValue ? nil : attributeValue); - attributes.push(attributeName); + attributes.push(themeAttribute); } } - CachedThemeAttributes[[self className]] = attributes; + themedCacheAttributes[theClassName] = attributes; return attributes; } @@ -230,23 +230,21 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (void)_loadThemeAttributes { var theClass = [self class], - attributes = [theClass _themeAttributes], + attributes = [theClass _themeAttributesForTheme:[self theme] andThemeClass:[self themeClass]], count = attributes.length; if (!count) return; - var theme = [self theme], - themeClass = [self themeClass]; - _themeAttributes = {}; while (count--) { - var attributeName = attributes[count--], - attribute = [_CPThemeAttribute attributeWithName:attributeName defaultValue:attributes[count] defaultAttribute:[theme attributeWithName:attributeName forClass:themeClass]]; + var attribute = attributes[count]; - _themeAttributes[attributeName] = attribute; + CPThemeAttributeAlloc[@"CPThemeAttributeAlloc0l"]++; + + _themeAttributes[attribute._name] = attribute; } } @@ -276,7 +274,7 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", for (var attributeName in _themeAttributes) { if (_themeAttributes.hasOwnProperty(attributeName)) - [_themeAttributes[attributeName] setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]]; + _themeAttributes[attributeName] = [_themeAttributes[attributeName] attributeBySettingParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]]; } } @@ -475,14 +473,14 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", var theClass = [self class], themeClass = [self themeClass], - attributes = [theClass _themeAttributes], + attributes = [theClass _themeAttributesForTheme:_theme andThemeClass:themeClass], count = attributes.length; while (count--) { - var attributeName = attributes[count--]; + var attribute = attributes[count]; - _themeAttributes[attributeName] = CPThemeAttributeDecode(aCoder, attributeName, attributes[count], _theme, themeClass); + _themeAttributes[attribute._name] = CPThemeAttributeDecode(aCoder, attribute); } } From 24889f7dcdfccf18d93e71b0768421234f3c2921 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Thu, 22 Oct 2015 19:51:16 +0200 Subject: [PATCH 06/12] Fixed: Adjust test cases --- Tests/AppKit/CPThemeTest.j | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/AppKit/CPThemeTest.j b/Tests/AppKit/CPThemeTest.j index e6fc1494c..f0e21e987 100644 --- a/Tests/AppKit/CPThemeTest.j +++ b/Tests/AppKit/CPThemeTest.j @@ -56,20 +56,20 @@ - (void)testThemeAttributeValueForState { - var themeAttribute = [[_CPThemeAttribute alloc] initWithName:@"test" defaultValue:5]; + var themeAttribute = [[_CPThemeAttribute alloc] initWithName:@"test" defaultValue:5 defaultAttribute:nil]; [self assertTrue:([themeAttribute valueForState:CPThemeState("aState")] == 5) message:"Return the default value for the theme attribute if the theme attribute has no value defined for the given state"]; - [themeAttribute setValue:7 forState:CPThemeState("aState")]; + themeAttribute = [themeAttribute attributeBySettingValue:7 forState:CPThemeState("aState")]; [self assertTrue:([themeAttribute valueForState:CPThemeState("aState")] == 7) message:"Return the correct value for the state if the state is defined"]; - [themeAttribute setValue:8 forState:CPThemeState('normal')]; + themeAttribute = [themeAttribute attributeBySettingValue:8 forState:CPThemeState('normal')]; [self assertTrue:([themeAttribute valueForState:CPThemeState("aState1")] == 8) message:"Return the normal value for the state if the theme attribute has no value defined for the given state but has a value for the normal state defined"]; - [themeAttribute setValue:10 forState:CPThemeState('aState3+aState4')]; + themeAttribute = [themeAttribute attributeBySettingValue:10 forState:CPThemeState('aState3+aState4')]; [self assertTrue:([themeAttribute valueForState:CPThemeState("aState3")] == 8) message:"Return the normal value for the state if the state is only a partial match on the theme attributes defined states"]; [self assertTrue:([themeAttribute valueForState:CPThemeState("aState4+aState3")] == 10) message:"Correctly match combined states on the theme attribute"]; - [themeAttribute setValue:9 forState:CPThemeState('aState3')]; + themeAttribute = [themeAttribute attributeBySettingValue:9 forState:CPThemeState('aState3')]; [self assertTrue:([themeAttribute valueForState:CPThemeState("aState8+aState3+aState4")] == 10) message:"Return the largest partial subset match for a combined state that isn't a perfect match"]; } From 790190e5c34be3c5343badfba176e39439ef12af Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 23 Oct 2015 10:39:52 +0200 Subject: [PATCH 07/12] Fixed: Removed code collecting statistic of theme attribute creation and some logging --- AppKit/CPTheme.j | 53 +--------------------------------------- AppKit/_CPObject+Theme.j | 14 +++-------- 2 files changed, 4 insertions(+), 63 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 8714833b7..f8f472e21 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -36,47 +36,6 @@ var CPThemesByName = { }, /*! @ingroup appkit */ - -CPThemeAttributeAlloc = { - CPThemeAttributeAlloc0: 0, - CPThemeAttributeAlloc0l: 0, - CPThemeAttributeAlloc0c: 0, - CPThemeAttributeAlloc1c: 0, - CPThemeAttributeAlloc0d: 0, - CPThemeAttributeAlloc0dd: 0, - CPThemeAttributeAlloc1: 0, - CPThemeAttributeAlloc2: 0, - CPThemeAttributeAlloc3: 0, - CPThemeAttributeAlloc4: 0, - CPThemeAttributeAlloc5: 0, - CPThemeAttributeAlloc6: 0, - CPThemeAttributeAlloc7: 0, - CPThemeAttributeAlloc8: 0, - CPThemeAttributeAlloc9: 0, - CPThemeAttributeAllocM: 0, - CPThemeAttributeAllocc0: 0, - CPThemeAttributeAllocc1: 0, - CPThemeAttributeAllocc2: 0, - CPThemeAttributeAllocc3: 0, - CPThemeAttributeAllocc4: 0, - CPThemeAttributeAllocc5: 0, - CPThemeAttributeAllocc6: 0, - CPThemeAttributeAllocc7: 0, - CPThemeAttributeAllocc8: 0, - CPThemeAttributeAllocc9: 0, - CPThemeAttributeAllocc10: 0, - CPThemeAttributeAllocc11: 0, - CPThemeAttributeAllocc12: 0, - CPThemeAttributeAllocc13: 0, - CPThemeAttributeAllocc14: 0, - CPThemeAttributeAllocc15: 0, - CPThemeAttributeAllocc16: 0, - CPThemeAttributeAllocc17: 0, - CPThemeAttributeAllocc18: 0, - CPThemeAttributeAllocc19: 0, - CPThemeAttributeAllocc20: 0, -} - @implementation CPTheme : CPObject { CPString _name; @@ -766,9 +725,6 @@ CPThemeAttributeCache = {}; // CPThemeAttributeCache[_name][themeDefaultAttribut if (anAttribute._values) mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy]; - var size = [mergedAttribute._values count]; - CPThemeAttributeAlloc[@"CPThemeAttributeAlloc" + (size < 10 ? size : @"M")]++; - return mergedAttribute; } @@ -808,8 +764,6 @@ var ParentAttributeForCoder = nil; state = CPThemeStateNormalString [_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state]; - - CPThemeAttributeAlloc[@"CPThemeAttributeAlloc1c"]++; } else { @@ -817,8 +771,6 @@ var ParentAttributeForCoder = nil; keys = [encodedValues allKeys], count = keys.length; - CPThemeAttributeAlloc[@"CPThemeAttributeAllocc" + (count < 20 ? count : @"M")]++; - while (count--) { var key = keys[count]; @@ -906,11 +858,8 @@ function CPThemeAttributeDecode(aCoder, attribute) ParentAttributeForCoder = nil; if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]]) - { attribute = [attribute attributeBySettingValue:decodedAttribute]; - - CPThemeAttributeAlloc[@"CPThemeAttributeAlloc0dd"]++; - } else + else attribute = decodedAttribute; } diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index 1d0359cb9..24a9163bf 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -242,8 +242,6 @@ var NULL_THEME = {}; { var attribute = attributes[count]; - CPThemeAttributeAlloc[@"CPThemeAttributeAlloc0l"]++; - _themeAttributes[attribute._name] = attribute; } } @@ -309,9 +307,7 @@ var NULL_THEME = {}; if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - var a = [themeAttr attributeBySettingValue:aValue forState:aState]; - //print("setValue:forThemeAttribute:inState:" + aState + ": " + aName + ": " + aValue + "oldAttribute: " + [themeAttr._values description]); - _themeAttributes[aName] = a; + _themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState]; } - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inStates:(CPArray)states @@ -329,9 +325,7 @@ var NULL_THEME = {}; if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - var a = [themeAttr attributeBySettingValue:aValue forState:aState]; - //print("setValue:forThemeAttribute:inStates:" + aState + ": " + aName + ": " + aValue + "oldAttribute: " + [themeAttr._values description]); - _themeAttributes[aName] = a; + _themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState]; } - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName @@ -341,9 +335,7 @@ var NULL_THEME = {}; if (!themeAttr) [CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"]; - var a = [themeAttr attributeBySettingValue:aValue]; - //print("setValue:forThemeAttribute:" + ": " + aName + ": " + aValue + "oldAttribute: " + [themeAttr._values description]); - _themeAttributes[aName] = a; + _themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue]; } - (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState From 1520c1ebab2e1d37ee626ae74aed071e54cfa1dd Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 23 Oct 2015 15:40:45 +0200 Subject: [PATCH 08/12] Fixed: Linting --- AppKit/CPTheme.j | 56 ++++++++++++++++++---------------------- AppKit/_CPObject+Theme.j | 5 ++-- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index f8f472e21..66bdbaca1 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -394,7 +394,8 @@ ThemeState.prototype.without = function(aState) var firstTransform = CPThemeWithoutTransform[this._stateNameString], result; - if (firstTransform) { + if (firstTransform) + { result = firstTransform[aState._stateNameString]; if (result) return result; @@ -426,7 +427,8 @@ ThemeState.prototype.and = function(aState) var firstTransform = CPThemeAndTransform[this._stateNameString], result; - if (firstTransform) { + if (firstTransform) + { result = firstTransform[aState._stateNameString]; if (result) return result; @@ -442,9 +444,9 @@ ThemeState.prototype.and = function(aState) return result; } -var CPThemeStates = {}; -var CPThemeWithoutTransform = {}; -var CPThemeAndTransform = {}; +var CPThemeStates = {}, + CPThemeWithoutTransform = {}, + CPThemeAndTransform = {}; ThemeState._cacheThemeState = function(aState) { @@ -606,41 +608,33 @@ CPThemeAttributeCache = {}; // CPThemeAttributeCache[_name][themeDefaultAttribut { var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; - if (aValue !== undefined && aValue !== nil) { + if (aValue !== undefined && aValue !== nil) attribute._values = @{ CPThemeStateNormalString: aValue }; - } return attribute; } - (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState { - var shouldRemoveValue = aValue === undefined || aValue === nil; + var shouldRemoveValue = aValue === undefined || aValue === nil, + attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute], + values = _values; - var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; - -// var values = [_values copy]; - var values = _values; - - if (values != null) - { - values = [values copy]; - if (shouldRemoveValue) - [values removeObjectForKey:String(aState)]; - else - [values setObject:aValue forKey:String(aState)]; - attribute._values = values; - } + if (values != null) + { + values = [values copy]; + if (shouldRemoveValue) + [values removeObjectForKey:String(aState)]; else - { - if (!shouldRemoveValue) - { - values = [[CPDictionary alloc] init]; - [values setObject:aValue forKey:String(aState)]; -// values = @{stateString: aValue}; - attribute._values = values; - } - } + [values setObject:aValue forKey:String(aState)]; + attribute._values = values; + } + else if (!shouldRemoveValue) + { + values = [[CPDictionary alloc] init]; + [values setObject:aValue forKey:String(aState)]; + attribute._values = values; + } return attribute; } diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index 24a9163bf..d4735ae08 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -413,14 +413,13 @@ var NULL_THEME = {}; value = attributeValueState[1], state = attributeValueState[2]; - if (state) { + if (state) if (state.isa && [state isKindOfClass:CPArray]) [self setValue:value forThemeAttribute:attribute inStates:state]; else [self setValue:value forThemeAttribute:attribute inState:state]; - } else { + else [self setValue:value forThemeAttribute:attribute]; - } } } From ed12bae6aacd7ed1a9dd05d42e47e2e589aae3eb Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 23 Oct 2015 16:38:12 +0200 Subject: [PATCH 09/12] Fixed: Removed unused global variable --- AppKit/CPTheme.j | 2 -- 1 file changed, 2 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 66bdbaca1..e92b08cef 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -561,8 +561,6 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); CPThemeStateNormalString = String(CPThemeStateNormal); -CPThemeAttributeCache = {}; // CPThemeAttributeCache[_name][themeDefaultAttribute._UID][_defaultValue] -> CPThemeAttribute - @implementation _CPThemeAttribute : CPObject { CPString _name; From 68f45beb207954f5588bd5b238461c936e4b15c8 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Tue, 27 Oct 2015 09:03:26 +0100 Subject: [PATCH 10/12] Fixed: Code formatting --- AppKit/CPTheme.j | 16 ++++++++++++++++ AppKit/Themes/BlendKit/BKThemeDescriptor.j | 14 ++++++++------ AppKit/_CPObject+Theme.j | 14 ++++---------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index e92b08cef..3c9808b82 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -326,6 +326,7 @@ function ThemeState(stateNames) { if (!stateNames.hasOwnProperty(key)) continue; + if (key !== 'normal') { this._stateNames[key] = true; @@ -343,8 +344,10 @@ function ThemeState(stateNames) this._stateNameString = stateNameKeys[0]; var stateNameLength = stateNameKeys.length; + for (var stateIndex = 1; stateIndex < stateNameLength; stateIndex++) this._stateNameString = this._stateNameString + "+" + stateNameKeys[stateIndex]; + this._stateNameCount = stateNameLength; } @@ -397,6 +400,7 @@ ThemeState.prototype.without = function(aState) if (firstTransform) { result = firstTransform[aState._stateNameString]; + if (result) return result; } @@ -430,6 +434,7 @@ ThemeState.prototype.and = function(aState) if (firstTransform) { result = firstTransform[aState._stateNameString]; + if (result) return result; } @@ -452,11 +457,13 @@ ThemeState._cacheThemeState = function(aState) { // We do this caching so themeState equality works. Basically, doing CPThemeState('foo+bar') === CPThemeState('bar', 'foo') will return true. var themeState = CPThemeStates[String(aState)]; + if (themeState === undefined) { themeState = aState; CPThemeStates[String(themeState)] = themeState; } + return themeState; } @@ -474,14 +481,17 @@ function CPThemeState() throw "CPThemeState() must be called with at least one string argument"; var themeState; + if (arguments.length === 1 && typeof arguments[0] === 'string') { themeState = CPThemeStates[arguments[0]]; + if (themeState !== undefined) return themeState; } var stateNames = {}; + for (var argIndex = 0; argIndex < arguments.length; argIndex++) { if (arguments[argIndex] === [CPNull null] || !arguments[argIndex]) @@ -493,12 +503,14 @@ function CPThemeState() { if (!arguments[argIndex]._stateNames.hasOwnProperty(stateName)) continue; + stateNames[stateName] = true; } } else { var allNames = arguments[argIndex].split('+'); + for (var nameIndex = 0; nameIndex < allNames.length; nameIndex++) stateNames[allNames[nameIndex]] = true; } @@ -580,6 +592,7 @@ CPThemeStateNormalString = String(CPThemeStateNormal); _cache = { }; _name = aName; _defaultValue = aDefaultValue; + if (aDefaultAttribute) _themeDefaultAttribute = aDefaultAttribute; } @@ -621,10 +634,12 @@ CPThemeStateNormalString = String(CPThemeStateNormal); if (values != null) { values = [values copy]; + if (shouldRemoveValue) [values removeObjectForKey:String(aState)]; else [values setObject:aValue forKey:String(aState)]; + attribute._values = values; } else if (!shouldRemoveValue) @@ -714,6 +729,7 @@ CPThemeStateNormalString = String(CPThemeStateNormal); var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; mergedAttribute._values = [_values copy]; + if (anAttribute._values) mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy]; diff --git a/AppKit/Themes/BlendKit/BKThemeDescriptor.j b/AppKit/Themes/BlendKit/BKThemeDescriptor.j index 188d24e46..1512c480e 100644 --- a/AppKit/Themes/BlendKit/BKThemeDescriptor.j +++ b/AppKit/Themes/BlendKit/BKThemeDescriptor.j @@ -231,14 +231,15 @@ var ItemSizes = { }, value = attributeValueState[1], state = attributeValueState[2]; - if (state) { + if (state) + { if (state.isa && [state isKindOfClass:CPArray]) [aView setValue:value forThemeAttribute:attribute inStates:state]; else [aView setValue:value forThemeAttribute:attribute inState:state]; - } else { - [aView setValue:value forThemeAttribute:attribute]; } + else + [aView setValue:value forThemeAttribute:attribute]; } } @@ -307,14 +308,15 @@ var ItemSizes = { }, } } - if (state) { + if (state) + { if (state.isa && [state isKindOfClass:CPArray]) [aView setValue:value forThemeAttribute:attribute inStates:state]; else [aView setValue:value forThemeAttribute:attribute inState:state]; - } else { - [aView setValue:value forThemeAttribute:attribute]; } + else + [aView setValue:value forThemeAttribute:attribute]; } } } diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index d4735ae08..610a8dd01 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -89,9 +89,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", aState = [states objectAtIndex:--i]; while (i > 0) - { aState = aState.and([states objectAtIndex:--i]); - } + return _themeState.hasThemeState(aState); } @@ -118,6 +117,7 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", #endif var oldThemeState = _themeState; + _themeState = _themeState.without(aState); if (oldThemeState === _themeState) @@ -132,9 +132,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", aState = [states objectAtIndex:--i]; while (i > 0) - { aState = aState.and([states objectAtIndex:--i]); - } + return [self setThemeState:aState]; } @@ -144,9 +143,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", aState = [states objectAtIndex:--i]; while (i > 0) - { aState = aState.and([states objectAtIndex:--i]); - } + return [self unsetThemeState:aState]; } @@ -316,9 +314,7 @@ var NULL_THEME = {}; aState = [states objectAtIndex:--i]; while (i > 0) - { aState = aState.and([states objectAtIndex:--i]); - } var themeAttr = _themeAttributes && _themeAttributes[aName]; @@ -359,9 +355,7 @@ var NULL_THEME = {}; aState = [states objectAtIndex:--i]; while (i > 0) - { aState = aState.and([states objectAtIndex:--i]); - } var themeAttr = _themeAttributes && _themeAttributes[aName]; From 42c629ca45dc57a59d1fa7277d0d0836c99c367b Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Tue, 27 Oct 2015 09:03:55 +0100 Subject: [PATCH 11/12] =?UTF-8?q?Fixed:=20Use=20=E2=80=99;=E2=80=99=20inst?= =?UTF-8?q?ead=20of=20=E2=80=99,=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppKit/CPTheme.j | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 3c9808b82..a9246cc89 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -758,8 +758,8 @@ var ParentAttributeForCoder = nil; _cache = {}; _name = [aCoder decodeObjectForKey:@"name"]; - _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"], - _values = @{}, + _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"]; + _values = @{}; _themeDefaultAttribute = ParentAttributeForCoder; if ([aCoder containsValueForKey:@"value"]) From 3240d6f11a2947851dd50cad8308dd0f20712822 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Fri, 30 Oct 2015 11:20:51 +0100 Subject: [PATCH 12/12] Fixed: Added TODO comments that deprecated use should be remove in future release --- AppKit/_CPObject+Theme.j | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AppKit/_CPObject+Theme.j b/AppKit/_CPObject+Theme.j index 610a8dd01..20f32da85 100644 --- a/AppKit/_CPObject+Theme.j +++ b/AppKit/_CPObject+Theme.j @@ -76,6 +76,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (BOOL)hasThemeState:(ThemeState)aState { #if DEBUG +// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as +// the preformance cost for this check is to high. We should remove this check in a future release. if (aState && aState.isa && [aState isKindOfClass:CPArray]) [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'hasThemeStates: instead: " + aState]; #endif @@ -97,6 +99,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (BOOL)setThemeState:(ThemeState)aState { #if DEBUG +// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as +// the preformance cost for this check is to high. We should remove this check in a future release. if (aState && aState.isa && [aState isKindOfClass:CPArray]) [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'setThemeStates: instead: " + aState]; #endif @@ -112,6 +116,8 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (BOOL)unsetThemeState:(ThemeState)aState { #if DEBUG +// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as +// the preformance cost for this check is to high. We should remove this check in a future release. if (aState && aState.isa && [aState isKindOfClass:CPArray]) [CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'unsetThemeStates: instead: " + aState]; #endif @@ -296,6 +302,8 @@ var NULL_THEME = {}; - (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState { #if DEBUG +// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as +// the preformance cost for this check is to high. We should remove this check in a future release. if (aState.isa && [aState isKindOfClass:CPArray]) [CPException raise:CPInvalidArgumentException reason:self + @": aState can't be an array. Please use 'setValue:forThemeAttribute:inStates:' instead: " + aState]; #endif