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/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/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/CPTextField.j b/AppKit/CPTextField.j index 0dbb30bde..09ed728f4 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -684,7 +684,16 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); // Select the text if the textfield became first responder through keyboard interaction if (!_willBecomeFirstResponderByClick) + { [self _selectText:self immediately:YES]; + } + else + { + var point = CGPointMake([self convertPointFromBase:[[CPApp currentEvent] locationInWindow]].x - [self currentValueForThemeAttribute:@"content-inset"].left, 0), + position = [CPPlatformString charPositionOfString:[self stringValue] withFont:[self font] forPoint:point]; + + [self setSelectedRange:CPMakeRange(position, 0)]; + } _willBecomeFirstResponderByClick = NO; diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j index 749b8fa4a..a9246cc89 100644 --- a/AppKit/CPTheme.j +++ b/AppKit/CPTheme.j @@ -36,7 +36,6 @@ var CPThemesByName = { }, /*! @ingroup appkit */ - @implementation CPTheme : CPObject { CPString _name; @@ -327,6 +326,7 @@ function ThemeState(stateNames) { if (!stateNames.hasOwnProperty(key)) continue; + if (key !== 'normal') { this._stateNames[key] = true; @@ -344,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; } @@ -392,7 +394,19 @@ ThemeState.prototype.without = function(aState) if (!aState || aState === [CPNull null]) return this; + var firstTransform = CPThemeWithoutTransform[this._stateNameString], + result; + + if (firstTransform) + { + result = firstTransform[aState._stateNameString]; + + if (result) + return result; + } + var newStates = {}; + for (var stateName in this._stateNames) { if (!this._stateNames.hasOwnProperty(stateName)) @@ -402,25 +416,54 @@ ThemeState.prototype.without = function(aState) newStates[stateName] = true; } - return ThemeState._cacheThemeState(new ThemeState(newStates)); + 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 CPThemeStates = {}, + CPThemeWithoutTransform = {}, + CPThemeAndTransform = {}; 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; } @@ -438,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]) @@ -457,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; } @@ -522,6 +570,9 @@ CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular"); CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall"); CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); +CPThemeStateNormalString = String(CPThemeStateNormal); + + @implementation _CPThemeAttribute : CPObject { CPString _name; @@ -532,7 +583,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); _CPThemeAttribute _themeDefaultAttribute; } -- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue +- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute { self = [super init]; @@ -541,7 +592,9 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); _cache = { }; _name = aName; _defaultValue = aDefaultValue; - _values = @{}; + + if (aDefaultAttribute) + _themeDefaultAttribute = aDefaultAttribute; } return self; @@ -562,24 +615,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); return [_values count] > 0; } -- (void)setValue:(id)aValue +- (_CPThemeAttribute)attributeBySettingValue:(id)aValue { - _cache = {}; + var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute]; - if (aValue === undefined || aValue === nil) - _values = @{}; - else - _values = @{ String(CPThemeStateNormal): aValue }; + if (aValue !== undefined && aValue !== nil) + attribute._values = @{ CPThemeStateNormalString: aValue }; + + return attribute; } -- (void)setValue:(id)aValue forState:(ThemeState)aState +- (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState { - _cache = { }; + var shouldRemoveValue = aValue === undefined || aValue === nil, + attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute], + values = _values; - if ((aValue === undefined) || (aValue === nil)) - [_values removeObjectForKey:String(aState)]; - else - [_values setObject:aValue forKey:String(aState)]; + 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) + { + values = [[CPDictionary alloc] init]; + [values setObject:aValue forKey:String(aState)]; + attribute._values = values; + } + + return attribute; } - (id)value @@ -604,7 +674,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); if (aState._stateNameCount > 1) { var states = [_values allKeys], - count = states.length, + count = states ? states.length : 0, largestThemeState = 0; while (count--) @@ -642,27 +712,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); 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 { - 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; } +- (CPString)description +{ + return [super description] + @" Name: " + _name + @", defaultAttribute: " + _themeDefaultAttribute + @", defaultValue: " + _defaultValue + @", values: " + _values; +} + @end + +// This is used to pass 'parrentAttribute' to the coder +var ParentAttributeForCoder = nil; + @implementation _CPThemeAttribute (CPCoding) - (id)initWithCoder:(CPCoder)aCoder @@ -676,13 +760,16 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); _name = [aCoder decodeObjectForKey:@"name"]; _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"]; _values = @{}; + _themeDefaultAttribute = ParentAttributeForCoder; 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]; } @@ -710,7 +797,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini"); [aCoder encodeObject:_defaultValue forKey:@"defaultValue"]; var keys = [_values allKeys], - count = keys.length; + count = keys ? keys.length : 0; if (count === 1) { @@ -748,7 +835,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute) { var state = [values allKeys][0]; - if (state === String(CPThemeStateNormal)) + if (state === CPThemeStateNormalString) { [aCoder encodeObject:[values objectForKey:state] forKey:key]; @@ -766,30 +853,24 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute) return NO; } -function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass) +function CPThemeAttributeDecode(aCoder, attribute) { - var key = "$a" + anAttributeName; + var key = "$a" + attribute._name; - if (![aCoder containsValueForKey:key]) - var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue]; - - else + if ([aCoder containsValueForKey:key]) { - var attribute = [aCoder decodeObjectForKey:key]; + ParentAttributeForCoder = attribute._themeDefaultAttribute; - if (!attribute || !attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]]) - { - var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue]; + var decodedAttribute = [aCoder decodeObjectForKey:key]; - [themeAttribute setValue:attribute]; + ParentAttributeForCoder = nil; - attribute = themeAttribute; - } + if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]]) + attribute = [attribute attributeBySettingValue:decodedAttribute]; + else + attribute = decodedAttribute; } - if (aTheme && aClass) - [attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]]; - return attribute; } diff --git a/AppKit/CPTokenField.j b/AppKit/CPTokenField.j index 3724b20a2..858b11495 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/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/Cib/CPCibLoading.j b/AppKit/Cib/CPCibLoading.j index fa5bd6956..7c346dd95 100644 --- a/AppKit/Cib/CPCibLoading.j +++ b/AppKit/Cib/CPCibLoading.j @@ -49,7 +49,7 @@ var CPCibOwner = @"CPCibOwner", + (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner { - return [self loadCibNamed:aName owner:anOwner loadDelegate:nil]; + return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner }]; } - (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable @@ -68,14 +68,7 @@ var CPCibOwner = @"CPCibOwner", + (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate { - if (![aName hasSuffix:@".cib"]) - aName = [aName stringByAppendingString:@".cib"]; - - // Path is based solely on anOwner: - var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle], - path = [bundle _cibPathForResource:aName]; - - return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate]; + return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate]; } - (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate @@ -101,6 +94,17 @@ var CPCibOwner = @"CPCibOwner", return [self pathForResource:aName]; } ++ (CPString)_cibPathForName:(CPString)aName withOwner:(id)anOwner +{ + if (![aName hasSuffix:@".cib"]) + aName = [aName stringByAppendingString:@".cib"]; + + // Path is based solely on anOwner: + var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle]; + + return [bundle _cibPathForResource:aName]; +} + @end @implementation _CPCibLoadDelegate : CPObject diff --git a/AppKit/Jakefile b/AppKit/Jakefile index 69f958e4c..b5aeb5434 100644 --- a/AppKit/Jakefile +++ b/AppKit/Jakefile @@ -33,8 +33,9 @@ appKitTask = framework ("AppKit", function(appKitTask) return "--include \"" + aFilename + "\""; }).join(" "); - if ($CONFIGURATION === "Release") - appKitTask.setCompilerFlags("-O " + INCLUDES); + if ($CONFIGURATION === "Release") { + appKitTask.setCompilerFlags("-O2 " + INCLUDES); + } else appKitTask.setCompilerFlags("-DDEBUG -g " + INCLUDES); }); diff --git a/AppKit/Platform/DOM/CPPlatformString.j b/AppKit/Platform/DOM/CPPlatformString.j index 1e520dd7f..844eeea3e 100644 --- a/AppKit/Platform/DOM/CPPlatformString.j +++ b/AppKit/Platform/DOM/CPPlatformString.j @@ -129,6 +129,33 @@ var DOMFixedWidthSpanElement = nil, DOMMetricsDivElement.appendChild(DOMMetricsImgElement); } ++ (int)charPositionOfString:(CPString)aString withFont:(CPFont)aFont forPoint:(CGPoint)aPoint +{ + if (!aString) + return 0; + + var position = 0, + stringLength = aString.length, + currentString = ""; + + for (var i = 0; i < stringLength; i++) + { + var lastChar = aString[i]; + + currentString += lastChar; + + var sizeOfString = [self sizeOfString:currentString withFont:aFont forWidth:nil].width, + sizeLastChar = [self sizeOfString:lastChar withFont:aFont forWidth:nil].width; + + if (sizeOfString - sizeLastChar / 2 < aPoint.x) + position++; + else + break; + } + + return position; +} + + (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth { if (!DOMFixedWidthSpanElement) 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/Themes/BlendKit/BKThemeDescriptor.j b/AppKit/Themes/BlendKit/BKThemeDescriptor.j index 801c6e6ae..1512c480e 100644 --- a/AppKit/Themes/BlendKit/BKThemeDescriptor.j +++ b/AppKit/Themes/BlendKit/BKThemeDescriptor.j @@ -232,7 +232,12 @@ var ItemSizes = { }, state = attributeValueState[2]; if (state) - [aView setValue:value forThemeAttribute:attribute inState: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]; } @@ -304,7 +309,12 @@ var ItemSizes = { }, } if (state) - [aView setValue:value forThemeAttribute:attribute inState: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/Themes/BlendKit/Jakefile b/AppKit/Themes/BlendKit/Jakefile index da9439c09..540297c1a 100644 --- a/AppKit/Themes/BlendKit/Jakefile +++ b/AppKit/Themes/BlendKit/Jakefile @@ -20,7 +20,7 @@ blendKitTask = framework ("BlendKit", function(blendKitTask) blendKitTask.setFlattensSources(true); // FIXME: how do we non flatten? if ($CONFIGURATION === "Release") - blendKitTask.setCompilerFlags("-O"); + blendKitTask.setCompilerFlags("-O2"); else blendKitTask.setCompilerFlags("-DDEBUG -g"); }); 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 e63dfffc2..20f32da85 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"; @@ -38,8 +36,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,31 +75,55 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", - (BOOL)hasThemeState:(ThemeState)aState { - if (aState.isa && [aState isKindOfClass:CPArray]) - return _themeState.hasThemeState.apply(_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 + + 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 +// 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]) - 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 +// 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]) - 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); if (oldThemeState === _themeState) @@ -107,6 +132,28 @@ 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 @@ -134,25 +181,30 @@ 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 = [], - 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) { attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes; - CachedThemeAttributes[[self className]] = attributes; - break; } @@ -167,38 +219,34 @@ 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); } } + themedCacheAttributes[theClassName] = attributes; + return attributes; } - (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 alloc] initWithName:attributeName defaultValue:attributes[count]]; + var attribute = attributes[count]; - [attribute setParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]]; - - _themeAttributes[attributeName] = attribute; + _themeAttributes[attribute._name] = attribute; } } @@ -228,7 +276,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]]; } } @@ -253,48 +301,96 @@ 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 +// 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 - 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]; + _themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState]; +} + +- (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 + "'"]; + + _themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState]; } - (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]; + _themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue]; } - (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 @@ -320,7 +416,10 @@ var CPViewThemeClassKey = @"CPViewThemeClassKey", state = attributeValueState[2]; if (state) - [self setValue:value forThemeAttribute:attribute inState: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]; } @@ -367,14 +466,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); } } diff --git a/Foundation/Jakefile b/Foundation/Jakefile index 20d218c9e..066d1341b 100644 --- a/Foundation/Jakefile +++ b/Foundation/Jakefile @@ -51,7 +51,7 @@ foundationTask = framework ("Foundation", function(foundationTask) INCLUDES = "--include \"../AppKit/Platform/Platform.h\" " + INCLUDES; if ($CONFIGURATION === "Release") - foundationTask.setCompilerFlags("-O " + INCLUDES); + foundationTask.setCompilerFlags("-O2 " + INCLUDES); else foundationTask.setCompilerFlags("-DDEBUG -g " + INCLUDES); }); diff --git a/Objective-J/Bootstrap.js b/Objective-J/Bootstrap.js index 58778f7d0..ddd7263bf 100644 --- a/Objective-J/Bootstrap.js +++ b/Objective-J/Bootstrap.js @@ -45,6 +45,23 @@ if (DOMBaseElementsCount > 0) pageURL = new CFURL(DOMBaseElementHref, pageURL); } +// Set compiler flags + +if (typeof OBJJ_COMPILER_FLAGS !== 'undefined') +{ + var flags = 0; + for (var i = 0; i < OBJJ_COMPILER_FLAGS.length; i++) + { + var flag = ObjJAcornCompiler.Flags[OBJJ_COMPILER_FLAGS[i]]; + + if (flag != null) + { + flags |= flag; + } + } + exports.setCurrentCompilerFlags(flags); +} + // Turn the main file into a URL. var mainFileURL = new CFURL(window.OBJJ_MAIN_FILE || "main.j"), diff --git a/Objective-J/CFHTTPRequest.js b/Objective-J/CFHTTPRequest.js index eff9a49df..a961cc67b 100644 --- a/Objective-J/CFHTTPRequest.js +++ b/Objective-J/CFHTTPRequest.js @@ -351,7 +351,7 @@ function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure, onprogress) { var aFilePath = aURL.toString().substring(5), OS = require("os"), - gccFlags = require("objective-j").currentCompilerFlags(), + gccFlags = require("objective-j").currentGccCompilerFlags(), chunk, fileContents = ""; diff --git a/Objective-J/CommonJS/lib/objective-j/compiler.js b/Objective-J/CommonJS/lib/objective-j/compiler.js index 34416cde0..6f7f91972 100644 --- a/Objective-J/CommonJS/lib/objective-j/compiler.js +++ b/Objective-J/CommonJS/lib/objective-j/compiler.js @@ -192,8 +192,12 @@ function resolveFlags(args) else if (argument.indexOf("-g") === 0) objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.IncludeDebugSymbols; - else if (argument.indexOf("-O") === 0) + else if (argument.indexOf("-O") === 0) { objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.Compress; + // FIXME: currently we are sending in '-O2' when we want InlineMsgSend. Here we only check if we it is '-O...' + if (argument.length > 2) + objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.InlineMsgSend; + } else if (argument.indexOf("-G") === 0) objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.Generate; diff --git a/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js b/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js index e71b9129f..613d9b254 100644 --- a/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js +++ b/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js @@ -907,7 +907,7 @@ BundleTask.prototype.defineSourceTasks = function() basePath = absolutePath.substring(0, absolutePath.length - theTranslatedFilename.length); // Here we set the current compiler flags so the load system will know what compiler flags to use - ObjectiveJ.setCurrentCompilerFlags(environmentCompilerFlags); + ObjectiveJ.setCurrentGccCompilerFlags(environmentCompilerFlags); // Here we tell the CFBundle to load frameworks for the current build enviroment and not the enviroment that is running CFBundle.environments = function() {return [anEnvironment.name(), "ObjJ"]}; ObjectiveJ.make_narwhal_factory(absolutePath, basePath, translateFilenameToPath)(require, e, module, system, print); diff --git a/Objective-J/FileExecutable.js b/Objective-J/FileExecutable.js index 8e9bcac7a..a3b471a7e 100644 --- a/Objective-J/FileExecutable.js +++ b/Objective-J/FileExecutable.js @@ -41,7 +41,7 @@ function FileExecutable(/*CFURL|String*/ aURL, /*Dictionary*/ aFilenameTranslate if (fileContents.match(/^@STATIC;/)) executable = decompile(fileContents, aURL); else if ((extension === "j" || !extension) && !fileContents.match(/^{/)) - executable = exports.ObjJAcornCompiler.compileFileDependencies(fileContents, aURL, ObjJAcornCompiler.Flags.IncludeDebugSymbols); + executable = exports.ObjJAcornCompiler.compileFileDependencies(fileContents, aURL, exports.currentCompilerFlags()); else executable = new Executable(fileContents, [], aURL); diff --git a/Objective-J/ObjJAcornCompiler.js b/Objective-J/ObjJAcornCompiler.js index fd7e0e1ec..89d1d6d76 100644 --- a/Objective-J/ObjJAcornCompiler.js +++ b/Objective-J/ObjJAcornCompiler.js @@ -365,7 +365,8 @@ var MethodDef = function(name, types) this.types = types; } -var currentCompilerFlags = ""; +var currentCompilerFlags = 0; +var currentGccCompilerFlags = ""; var reservedIdentifiers = exports.acorn.makePredicate("self _cmd undefined localStorage arguments"); @@ -414,14 +415,12 @@ var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned* } this.dependencies = []; - this.flags = flags | ObjJAcornCompiler.Flags.IncludeDebugSymbols; + this.flags = flags & (ObjJAcornCompiler.Flags.IncludeDebugSymbols | ObjJAcornCompiler.Flags.InlineMsgSend | ObjJAcornCompiler.Flags.IncludeTypeSignatures); this.classDefs = classDefs ? classDefs : Object.create(null); this.protocolDefs = protocolDefs ? protocolDefs : Object.create(null); this.typeDefs = typeDefs ? typeDefs : Object.create(null); this.lastPos = 0; - if (currentCompilerFlags & ObjJAcornCompiler.Flags.Generate) - this.generate = true; - this.generate = true; + this.generate = true; // Before there was an option to generate the code or copy & paste it from the source. Today we always generate the code. compile(this.tokens, new Scope(null ,{ compiler: this }), pass === 2 ? pass2 : pass1); } @@ -487,7 +486,42 @@ ObjJAcornCompiler.prototype.compilePass2 = function() return this.jsBuffer.toString(); } -var currentCompilerFlags = ""; +exports.setCurrentGccCompilerFlags = function(/*String*/ compilerFlags) +{ + if (currentGccCompilerFlags === compilerFlags) return; + + currentGccCompilerFlags = compilerFlags; + + var args = compilerFlags.split(" "), + count = args.length, + objjcFlags = 0; + + for (var index = 0; index < count; ++index) + { + var argument = args[index]; + + if (argument.indexOf("-g") === 0) + objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.IncludeDebugSymbols; + + else if (argument.indexOf("-O") === 0) { + objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.Compress; + // FIXME: currently we are sending in '-O2' when we want InlineMsgSend. Here we only check if we it is '-O...'. + // Maybe we should have some other option for this + if (argument.length > 2) + objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.InlineMsgSend; + } + + else if (argument.indexOf("-G") === 0) + objjcFlags |= ObjectiveJ.ObjJAcornCompiler.Flags.Generate; + } + + currentCompilerFlags = objjcFlags; +} + +exports.currentGccCompilerFlags = function(/*String*/ compilerFlags) +{ + return currentGccCompilerFlags; +} exports.setCurrentCompilerFlags = function(/*String*/ compilerFlags) { @@ -504,6 +538,7 @@ ObjJAcornCompiler.Flags = { }; ObjJAcornCompiler.Flags.IncludeDebugSymbols = 1 << 0; ObjJAcornCompiler.Flags.IncludeTypeSignatures = 1 << 1; ObjJAcornCompiler.Flags.Generate = 1 << 2; +ObjJAcornCompiler.Flags.InlineMsgSend = 1 << 3; ObjJAcornCompiler.prototype.addWarning = function(/* Warning */ aWarning) { @@ -1626,64 +1661,170 @@ Literal: function(node, st, c) { }, ArrayLiteral: function(node, st, c) { var compiler = st.compiler, - generate = compiler.generate; + generate = compiler.generate, + buffer = compiler.jsBuffer; + if (!generate) { - compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, node.start)); + buffer.concat(compiler.source.substring(compiler.lastPos, node.start)); compiler.lastPos = node.start; } if (!generate) buffer.concat(" "); // Add an extra space if it looks something like this: "return()". No space between return and expression. + if (!st.receiverLevel) st.receiverLevel = 0; if (!node.elements.length) { - compiler.jsBuffer.concat("objj_msgSend(objj_msgSend(CPArray, \"alloc\"), \"init\")"); + if (compiler.flags & ObjJAcornCompiler.Flags.InlineMsgSend) { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = (CPArray.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPArray, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : (___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.method_msgSend[\"init\"] || _objj_forward)(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"init\"))"); + } else { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = CPArray.isa.objj_msgSend0(CPArray, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.objj_msgSend0(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"init\"))"); + } + + if (!(st.maxReceiverLevel >= st.receiverLevel)) + st.maxReceiverLevel = st.receiverLevel; } else { - compiler.jsBuffer.concat("objj_msgSend(objj_msgSend(CPArray, \"alloc\"), \"initWithObjects:count:\", ["); + if (compiler.flags & ObjJAcornCompiler.Flags.InlineMsgSend) { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = (CPArray.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPArray, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : (___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.method_msgSend[\"initWithObjects:count:\"] || _objj_forward)(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"initWithObjects:count:\", ["); + } else { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = CPArray.isa.objj_msgSend0(CPArray, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.objj_msgSend2(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"initWithObjects:count:\", ["); + } + + if (!(st.maxReceiverLevel >= st.receiverLevel)) + st.maxReceiverLevel = st.receiverLevel; + for (var i = 0; i < node.elements.length; i++) { var elt = node.elements[i]; if (i) - compiler.jsBuffer.concat(", "); + buffer.concat(", "); if (!generate) compiler.lastPos = elt.start; c(elt, st, "Expression"); - if (!generate) compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, elt.end)); + if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, elt.end)); } - compiler.jsBuffer.concat("], " + node.elements.length + ")"); + buffer.concat("], " + node.elements.length + "))"); } + st.receiverLevel--; if (!generate) compiler.lastPos = node.end; }, DictionaryLiteral: function(node, st, c) { var compiler = st.compiler, - generate = compiler.generate; + generate = compiler.generate, + buffer = compiler.jsBuffer, + noOfKeys = node.keys.length; + if (!generate) { - compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, node.start)); + buffer.concat(compiler.source.substring(compiler.lastPos, node.start)); compiler.lastPos = node.start; } if (!generate) buffer.concat(" "); // Add an extra space if it looks something like this: "return()". No space between return and expression. - if (!node.keys.length) { - compiler.jsBuffer.concat("objj_msgSend(objj_msgSend(CPDictionary, \"alloc\"), \"init\")"); + if (!st.receiverLevel) st.receiverLevel = 0; + if (!noOfKeys) { + if (compiler.flags & ObjJAcornCompiler.Flags.InlineMsgSend) { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = (CPDictionary.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPDictionary, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : (___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.method_msgSend[\"init\"] || _objj_forward)(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"init\"))"); + } else { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = CPDictionary.isa.objj_msgSend0(CPDictionary, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.objj_msgSend0(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"init\"))"); + } + + if (!(st.maxReceiverLevel >= st.receiverLevel)) + st.maxReceiverLevel = st.receiverLevel; } else { - compiler.jsBuffer.concat("objj_msgSend(objj_msgSend(CPDictionary, \"alloc\"), \"initWithObjectsAndKeys:\""); - for (var i = 0; i < node.keys.length; i++) { - var key = node.keys[i], - value = node.values[i]; + if (compiler.flags & ObjJAcornCompiler.Flags.InlineMsgSend) { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = (CPDictionary.isa.method_msgSend[\"alloc\"] || _objj_forward)(CPDictionary, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : (___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.method_msgSend[\"initWithObjects:forKeys:\"] || _objj_forward)(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"initWithObjects:forKeys:\", ["); + } else { + buffer.concat("(___r"); + buffer.concat(++st.receiverLevel + ""); + buffer.concat(" = CPDictionary.isa.objj_msgSend0(CPDictionary, \"alloc\"), ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(" == null ? null : ___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(".isa.objj_msgSend2(___r"); + buffer.concat(st.receiverLevel + ""); + buffer.concat(", \"initWithObjects:forKeys:\", ["); + } - compiler.jsBuffer.concat(", "); + if (!(st.maxReceiverLevel >= st.receiverLevel)) + st.maxReceiverLevel = st.receiverLevel; + for (var i = 0; i < noOfKeys; i++) { + var value = node.values[i]; + + if (i) buffer.concat(", "); if (!generate) compiler.lastPos = value.start; c(value, st, "Expression"); - if (!generate) compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, value.end)); + if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, value.end)); + } - compiler.jsBuffer.concat(", "); + buffer.concat("], ["); + for (var i = 0; i < noOfKeys; i++) { + var key = node.keys[i]; + + if (i) buffer.concat(", "); if (!generate) compiler.lastPos = key.start; c(key, st, "Expression"); - if (!generate) compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, key.end)); + if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, key.end)); } - compiler.jsBuffer.concat(")"); + buffer.concat("]))"); } + st.receiverLevel--; if (!generate) compiler.lastPos = node.end; }, ImportStatement: function(node, st, c) { @@ -2257,17 +2398,48 @@ MethodDeclarationStatement: function(node, st, c) { MessageSendExpression: function(node, st, c) { var compiler = st.compiler, generate = compiler.generate, + inlineMsgSend = compiler.flags & ObjJAcornCompiler.Flags.InlineMsgSend, buffer = compiler.jsBuffer, - nodeObject = node.object; + nodeObject = node.object, + selectors = node.selectors, + arguments = node.arguments, + argumentsLength = arguments.length, + firstSelector = selectors[0], + selector = firstSelector ? firstSelector.name : ""; // There is always at least one selector + + // Put together the selector. Maybe this should be done in the parser... + for (var i = 0; i < argumentsLength; i++) + if (i === 0) + selector += ":"; + else + selector += (selectors[i] ? selectors[i].name : "") + ":"; + if (!generate) { buffer.concat(compiler.source.substring(compiler.lastPos, node.start)); compiler.lastPos = nodeObject ? nodeObject.start : node.arguments.length ? node.arguments[0].start : node.end; + } else if (!inlineMsgSend) { + // Find out the total number of arguments so we can choose appropriate msgSend function. Only needed if call the function and not inline it + var totalNoOfParameters = argumentsLength; + + if (node.parameters) + totalNoOfParameters += node.parameters.length; } if (node.superObject) { if (!generate) buffer.concat(" "); // Add an extra space if it looks something like this: "return()". No space between return and expression. - buffer.concat("objj_msgSendSuper("); - buffer.concat("{ receiver:self, super_class:" + (st.currentMethodType() === "+" ? compiler.currentSuperMetaClass : compiler.currentSuperClass ) + " }"); + if (inlineMsgSend) { + buffer.concat("("); + buffer.concat(st.currentMethodType() === "+" ? compiler.currentSuperMetaClass : compiler.currentSuperClass); + buffer.concat(".method_dtable[\""); + buffer.concat(selector); + buffer.concat("\"] || _objj_forward)(self"); + } else { + buffer.concat("objj_msgSendSuper"); + if (totalNoOfParameters < 4) { + buffer.concat("" + totalNoOfParameters); + } + buffer.concat("({ receiver:self, super_class:" + (st.currentMethodType() === "+" ? compiler.currentSuperMetaClass : compiler.currentSuperClass ) + " }"); + } } else { @@ -2293,6 +2465,8 @@ MessageSendExpression: function(node, st, c) { c(nodeObject, st, "Expression"); buffer.concat(" == null ? null : "); } + if (inlineMsgSend) + buffer.concat("("); c(nodeObject, st, "Expression"); } else { receiverIsNotSelf = true; @@ -2303,12 +2477,20 @@ MessageSendExpression: function(node, st, c) { c(nodeObject, st, "Expression"); buffer.concat("), ___r"); buffer.concat(st.receiverLevel + ""); - buffer.concat(" == null ? null : ___r"); + buffer.concat(" == null ? null : "); + if (inlineMsgSend) + buffer.concat("("); + buffer.concat("___r"); buffer.concat(st.receiverLevel + ""); if (!(st.maxReceiverLevel >= st.receiverLevel)) st.maxReceiverLevel = st.receiverLevel; } - buffer.concat(".isa.objj_msgSend"); + if (inlineMsgSend) { + buffer.concat(".isa.method_msgSend[\""); + buffer.concat(selector); + buffer.concat("\"] || _objj_forward)"); + } else + buffer.concat(".isa.objj_msgSend"); } else { buffer.concat(" "); // Add an extra space if it looks something like this: "return()". No space between return and expression. buffer.concat("objj_msgSend("); @@ -2316,19 +2498,11 @@ MessageSendExpression: function(node, st, c) { } } - var selectors = node.selectors, - arguments = node.arguments, - argumentsLength = arguments.length, - firstSelector = selectors[0], - selector = firstSelector ? firstSelector.name : ""; // There is always at least one selector - if (generate && !node.superObject) { - var totalNoOfParameters = argumentsLength; - - if (node.parameters) - totalNoOfParameters += node.parameters.length; - if (totalNoOfParameters < 4) { - buffer.concat("" + totalNoOfParameters); + if (!inlineMsgSend) { + if (totalNoOfParameters < 4) { + buffer.concat("" + totalNoOfParameters); + } } if (receiverIsIdentifier) { @@ -2340,13 +2514,6 @@ MessageSendExpression: function(node, st, c) { } } - // Put together the selector. Maybe this should be done in the parser... - for (var i = 0; i < argumentsLength; i++) - if (i === 0) - selector += ":"; - else - selector += (selectors[i] ? selectors[i].name : "") + ":"; - buffer.concat(", \""); buffer.concat(selector); // FIXME: sel_getUid(selector + "") ? This FIXME is from the old preprocessor compiler buffer.concat("\""); diff --git a/Objective-J/Runtime.js b/Objective-J/Runtime.js index e352cf3e1..2c07e2154 100644 --- a/Objective-J/Runtime.js +++ b/Objective-J/Runtime.js @@ -49,9 +49,12 @@ GLOBAL(objj_ivar) = function(/*String*/ aName, /*String*/ aType) GLOBAL(objj_method) = function(/*String*/ aName, /*IMP*/ anImplementation, /*Array*/ types) { - this.name = aName; - this.method_imp = anImplementation; - this.types = types; + var method = anImplementation || function(/*id*/ aReceiver, /*SEL*/ aSelector) {CPException.isa.objj_msgSend2(CPException, "raise:reason:", CPInternalInconsistencyException, aReceiver.isa.method_msgSend0(self, "className") + " does not have an implementation for selector '" + aSelector + "'")}; + method.method_name = aName; + method.method_imp = anImplementation; + method.method_types = types; + + return method; } GLOBAL(objj_class) = function(displayName) @@ -197,7 +200,7 @@ GLOBAL(class_addMethod) = function(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anIm #if DEBUG // Give this function a "pretty" name for the console. - method.method_imp.displayName = METHOD_DISPLAY_NAME(aClass, method); + method.displayName = METHOD_DISPLAY_NAME(aClass, method); #endif // FIXME: Should this be done here? @@ -222,11 +225,11 @@ GLOBAL(class_addMethods) = function(/*Class*/ aClass, /*Array*/ methods) // FIXME: Don't do it if it exists? method_list.push(method); - method_dtable[method.name] = method; + method_dtable[method.method_name] = method; #if DEBUG // Give this function a "pretty" name for the console. - method.method_imp.displayName = METHOD_DISPLAY_NAME(aClass, method); + method.displayName = METHOD_DISPLAY_NAME(aClass, method); #endif } @@ -292,12 +295,22 @@ GLOBAL(class_replaceMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector, /*IM return NULL; var method = aClass.method_dtable[aSelector], - method_imp = NULL; + method_imp = method.method_imp, + new_method = new objj_method(method.method_name, aMethodImplementation, method.method_types); - if (method) - method_imp = method.method_imp; + new_method.displayName = method.displayName; + aClass.method_dtable[aSelector] = new_method; - method.method_imp = aMethodImplementation; + var index = aClass.method_list.indexOf(method); + + if (index !== -1) + { + aClass.method_list[index] = new_method; + } + else + { + aClass.method_list.push(new_method); + } return method_imp; } @@ -419,7 +432,7 @@ GLOBAL(protocol_addMethodDescriptions) = function(/*Protocol*/ proto, /*Array*/ { var method = methods[index]; - method_dtable[method.name] = method; + method_dtable[method.method_name] = method; } } @@ -491,26 +504,43 @@ var _class_initialize = function(/*Class*/ aClass) meta.objj_msgSend2 = objj_msgSendFast2; meta.objj_msgSend3 = objj_msgSendFast3; + aClass.method_msgSend = aClass.method_dtable; + meta.method_msgSend = meta.method_dtable; + meta.objj_msgSend0(aClass, "initialize"); CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING); } } -var _objj_forward = function(self, _cmd) +GLOBAL(_objj_forward) = function(self, _cmd) { var isa = self.isa, - implementation = isa.method_dtable[SEL_forwardingTargetForSelector_]; + meta = GETMETA(isa); + + if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING)) + { + _class_initialize(isa); + } + + var implementation = isa.method_msgSend[_cmd]; if (implementation) { - var target = implementation.method_imp.call(this, self, SEL_forwardingTargetForSelector_, _cmd); + return implementation.apply(isa, arguments); + } + + implementation = isa.method_dtable[SEL_forwardingTargetForSelector_]; + + if (implementation) + { + var target = implementation(self, SEL_forwardingTargetForSelector_, _cmd); if (target && target !== self) { arguments[0] = target; - return objj_msgSend.apply(this, arguments); + return target.isa.objj_msgSend.apply(target.isa, arguments); } } @@ -522,7 +552,7 @@ var _objj_forward = function(self, _cmd) if (forwardInvocationImplementation) { - var signature = implementation.method_imp.call(this, self, SEL_methodSignatureForSelector_, _cmd); + var signature = implementation(self, SEL_methodSignatureForSelector_, _cmd); if (signature) { @@ -541,7 +571,7 @@ var _objj_forward = function(self, _cmd) invocationIsa.objj_msgSend2(invocation, SEL_setArgument_atIndex_, arguments[index], index); } - forwardInvocationImplementation.method_imp.call(this, self, SEL_forwardInvocation_, invocation); + forwardInvocationImplementation(self, SEL_forwardInvocation_, invocation); return invocation == null ? null : invocationIsa.objj_msgSend0(invocation, SEL_returnValue); } @@ -552,7 +582,7 @@ var _objj_forward = function(self, _cmd) implementation = isa.method_dtable[SEL_doesNotRecognizeSelector_]; if (implementation) - return implementation.method_imp.call(this, self, SEL_doesNotRecognizeSelector_, _cmd); + return implementation(self, SEL_doesNotRecognizeSelector_, _cmd); throw class_getName(isa) + " does not implement doesNotRecognizeSelector:. Did you forget a superclass for " + class_getName(isa) + "?"; }; @@ -562,9 +592,7 @@ var _objj_forward = function(self, _cmd) if (!ISINITIALIZED(aClass))\ _class_initialize(aClass);\ \ - var method = aClass.method_dtable[aSelector];\ - \ - aMethodImplementation = method ? method.method_imp : _objj_forward; + aMethodImplementation = aClass.method_dtable[aSelector] || _objj_forward; GLOBAL(class_getMethodImplementation) = function(/*Class*/ aClass, /*SEL*/ aSelector) { @@ -845,11 +873,28 @@ GLOBAL(objj_msgSendSuper) = function(/*id*/ aSuper, /*SEL*/ aSelector) return implementation.apply(aSuper.receiver, arguments); } +GLOBAL(objj_msgSendSuper0) = function(/*id*/ aSuper, /*SEL*/ aSelector) +{ + return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector); +} + +GLOBAL(objj_msgSendSuper1) = function(/*id*/ aSuper, /*SEL*/ aSelector, arg0) +{ + return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector, arg0); +} + +GLOBAL(objj_msgSendSuper2) = function(/*id*/ aSuper, /*SEL*/ aSelector, arg0, arg1) +{ + return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector, arg0, arg1); +} + +GLOBAL(objj_msgSendSuper3) = function(/*id*/ aSuper, /*SEL*/ aSelector, arg0, arg1, arg2) +{ + return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector, arg0, arg1, arg2); +} + GLOBAL(objj_msgSendFast) = function(/*id*/ aReceiver, /*SEL*/ aSelector) { - var method = this.method_dtable[aSelector], - implementation = method ? method.method_imp : _objj_forward; - #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); @@ -857,7 +902,7 @@ GLOBAL(objj_msgSendFast) = function(/*id*/ aReceiver, /*SEL*/ aSelector) try { #endif - return implementation.apply(aReceiver, arguments); + return (this.method_dtable[aSelector] || _objj_forward).apply(aReceiver, arguments); #ifdef MAXIMUM_RECURSION_CHECKS } finally { @@ -874,9 +919,6 @@ var objj_msgSendFastInitialize = function(/*id*/ aReceiver, /*SEL*/ aSelector) GLOBAL(objj_msgSendFast0) = function(/*id*/ aReceiver, /*SEL*/ aSelector) { - var method = this.method_dtable[aSelector], - implementation = method ? method.method_imp : _objj_forward; - #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); @@ -884,7 +926,7 @@ GLOBAL(objj_msgSendFast0) = function(/*id*/ aReceiver, /*SEL*/ aSelector) try { #endif - return implementation(aReceiver, aSelector); + return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector); #ifdef MAXIMUM_RECURSION_CHECKS } finally { @@ -901,9 +943,6 @@ var objj_msgSendFast0Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector) GLOBAL(objj_msgSendFast1) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0) { - var method = this.method_dtable[aSelector], - implementation = method ? method.method_imp : _objj_forward; - #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); @@ -911,7 +950,7 @@ GLOBAL(objj_msgSendFast1) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0) try { #endif - return implementation(aReceiver, aSelector, arg0); + return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector, arg0); #ifdef MAXIMUM_RECURSION_CHECKS } finally { @@ -928,9 +967,6 @@ var objj_msgSendFast1Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector, GLOBAL(objj_msgSendFast2) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, arg1) { - var method = this.method_dtable[aSelector], - implementation = method ? method.method_imp : _objj_forward; - #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); @@ -938,7 +974,7 @@ GLOBAL(objj_msgSendFast2) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, try { #endif - return implementation(aReceiver, aSelector, arg0, arg1); + return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector, arg0, arg1); #ifdef MAXIMUM_RECURSION_CHECKS } finally { @@ -955,9 +991,6 @@ var objj_msgSendFast2Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector, GLOBAL(objj_msgSendFast3) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, arg1, arg2) { - var method = this.method_dtable[aSelector], - implementation = method ? method.method_imp : _objj_forward; - #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); @@ -965,7 +998,7 @@ GLOBAL(objj_msgSendFast3) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, try { #endif - return implementation(aReceiver, aSelector, arg0, arg1, arg2); + return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector, arg0, arg1, arg2); #ifdef MAXIMUM_RECURSION_CHECKS } finally { @@ -984,7 +1017,12 @@ var objj_msgSendFast3Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector, GLOBAL(method_getName) = function(/*Method*/ aMethod) { - return aMethod.name; + return aMethod.method_name; +} + +GLOBAL(method_getTypes) = function(/*Method*/ aMethod) +{ + return aMethod.method_types; } GLOBAL(method_getImplementation) = function(/*Method*/ aMethod) @@ -1050,6 +1088,7 @@ objj_class.prototype.objj_msgSend0 = objj_msgSendFast0Initialize; objj_class.prototype.objj_msgSend1 = objj_msgSendFast1Initialize; objj_class.prototype.objj_msgSend2 = objj_msgSendFast2Initialize; objj_class.prototype.objj_msgSend3 = objj_msgSendFast3Initialize; +objj_class.prototype.method_msgSend = Object.create(null); var SEL_description = sel_getUid("description"), SEL_forwardingTargetForSelector_ = sel_getUid("forwardingTargetForSelector:"), 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"]; } diff --git a/Tests/AppKit/CPViewTest.j b/Tests/AppKit/CPViewTest.j index 9c11b4de5..5131a2fc3 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"]; } @@ -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"]; diff --git a/Tests/Manual/SmartFoldersDemo/BadgedOutlineView.j b/Tests/Manual/SmartFoldersDemo/BadgedOutlineView.j index 69fd91f91..62c3142e1 100644 --- a/Tests/Manual/SmartFoldersDemo/BadgedOutlineView.j +++ b/Tests/Manual/SmartFoldersDemo/BadgedOutlineView.j @@ -36,21 +36,21 @@ var CPSourceListDataSource_sourceList_itemHasBadge_ = 1 << 1, - (void)setSourceListDataSource:(id)aDataSource { _sourceListDataSource = aDataSource; - implementedSourceListDataSourceMethods = 0; + _implementedSourceListDataSourceMethods = 0; if ([_sourceListDataSource respondsToSelector:@selector(sourceList:itemHasBadge:)]) - implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_itemHasBadge_; + _implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_itemHasBadge_; if ([_sourceListDataSource respondsToSelector:@selector(sourceList:badgeValueForItem:)]) - implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_badgeValueForItem_; + _implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_badgeValueForItem_; if ([_sourceListDataSource respondsToSelector:@selector(sourceList:badgeBackgroundColorForItem:)]) - implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_badgeBackgroundColorForItem_; + _implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_badgeBackgroundColorForItem_; if ([_sourceListDataSource respondsToSelector:@selector(sourceList:badgeTextColorForItem:)]) - implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_badgeTextColorForItem_; + _implementedSourceListDataSourceMethods |= CPSourceListDataSource_sourceList_badgeTextColorForItem_; } - (BOOL)itemHasBadge:(id)item { - if (implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_itemHasBadge_) + if (_implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_itemHasBadge_) return [_sourceListDataSource sourceList:self itemHasBadge:item]; return NO; @@ -58,7 +58,7 @@ var CPSourceListDataSource_sourceList_itemHasBadge_ = 1 << 1, - (CPInteger)badgeValueForItem:(id)item { - if ([self itemHasBadge:item] && implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_badgeValueForItem_) + if ([self itemHasBadge:item] && _implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_badgeValueForItem_) return [_sourceListDataSource sourceList:self badgeValueForItem:item]; return CPNotFound; @@ -137,7 +137,7 @@ var CPSourceListDataSource_sourceList_itemHasBadge_ = 1 << 1, textColor = [CPColor whiteColor]; //If the data source returns a custom colour.. - if (implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_badgeBackgroundColorForItem_) + if (_implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_badgeBackgroundColorForItem_) { backgroundColor = [_sourceListDataSource sourceList:self badgeBackgroundColorForItem:rowItem]; @@ -148,7 +148,7 @@ var CPSourceListDataSource_sourceList_itemHasBadge_ = 1 << 1, backgroundColor = BADGE_BACKGROUND_COLOR; //If the delegate wants a custom badge text colour.. - if (implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_badgeTextColorForItem_) + if (_implementedSourceListDataSourceMethods & CPSourceListDataSource_sourceList_badgeTextColorForItem_) { textColor = [_sourceListDataSource sourceList:self badgeTextColorForItem:rowItem]; diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/colon-selector-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/colon-selector-inlined.js new file mode 100644 index 000000000..bae53d471 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/colon-selector-inlined.js @@ -0,0 +1,2 @@ + +(object==null?null:(object.isa.method_msgSend[":"]||_objj_forward)(object,":",argument)); \ No newline at end of file diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/complex-receiver-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/complex-receiver-inlined.js new file mode 100644 index 000000000..b629b2a0b --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/complex-receiver-inlined.js @@ -0,0 +1,2 @@ +var a=0; +((___r1=a++<2?small_target:big_target),___r1==null?null:(___r1.isa.method_msgSend["doStuff"]||_objj_forward)(___r1,"doStuff")); diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/keyword-in-selector-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/keyword-in-selector-inlined.js new file mode 100644 index 000000000..fe6d6d930 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/keyword-in-selector-inlined.js @@ -0,0 +1 @@ +(object==null?null:(object.isa.method_msgSend["for:in:nil:"]||_objj_forward)(object,"for:in:nil:",a,b,nil)); \ No newline at end of file diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/multiple-parameters-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/multiple-parameters-inlined.js new file mode 100644 index 000000000..a28089dda --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/multiple-parameters-inlined.js @@ -0,0 +1,2 @@ + +(object==null?null:(object.isa.method_msgSend["label:label2:label3:label4:"]||_objj_forward)(object,"label:label2:label3:label4:",argument,argument2,argument3,argument4)); diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/no-parameters-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/no-parameters-inlined.js new file mode 100644 index 000000000..50c8b1a87 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/no-parameters-inlined.js @@ -0,0 +1,2 @@ +(CPArray.isa.method_msgSend["new"]||_objj_forward)(CPArray,"new"); +(object==null?null:(object.isa.method_msgSend["message"]||_objj_forward)(object,"message")); \ No newline at end of file diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/one-parameter-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/one-parameter-inlined.js new file mode 100644 index 000000000..970155db6 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/one-parameter-inlined.js @@ -0,0 +1,2 @@ +(CPArray.isa.method_msgSend["arrayWithArray:"]||_objj_forward)(CPArray,"arrayWithArray:",[]); +(object==null?null:(object.isa.method_msgSend["label:"]||_objj_forward)(object,"label:",argument)); \ No newline at end of file diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/self-as-receiver-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/self-as-receiver-inlined.js new file mode 100644 index 000000000..1235ab1bc --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/self-as-receiver-inlined.js @@ -0,0 +1,11 @@ +var the_class=objj_allocateClassPair(Nil,"MyClass"),meta_class=the_class.isa; +objj_registerClassPair(the_class); +class_addMethods(the_class,[new objj_method(sel_getUid("mySelector"),function $MyClass__mySelector(_1,_2){ +(_1.isa.method_msgSend["init"]||_objj_forward)(_1,"init"); +_1=nil; +(_1==null?null:(_1.isa.method_msgSend["init"]||_objj_forward)(_1,"init")); +},["id"]),new objj_method(sel_getUid("mySelector2"),function $MyClass__mySelector2(_3,_4){ +(_3.isa.method_msgSend["init"]||_objj_forward)(_3,"init"); +eval("self = null;"); +(_3==null?null:(_3.isa.method_msgSend["init"]||_objj_forward)(_3,"init")); +},["id"])]); \ No newline at end of file diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Messages/ternary-operator-argument-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Messages/ternary-operator-argument-inlined.js new file mode 100644 index 000000000..e3159dd7c --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Messages/ternary-operator-argument-inlined.js @@ -0,0 +1,4 @@ + +(object==null?null:(object.isa.method_msgSend["label:"]||_objj_forward)(object,"label:",condition?true:false)); +(object==null?null:(object.isa.method_msgSend["label:"]||_objj_forward)(object,"label:",condition?true:false)); +(object==null?null:(object.isa.method_msgSend["label:"]||_objj_forward)(object,"label:",condition?true:false)); diff --git a/Tests/Objective-J/Preprocessor/OutputTests/Misc/parenthesis-return-inlined.js b/Tests/Objective-J/Preprocessor/OutputTests/Misc/parenthesis-return-inlined.js new file mode 100644 index 000000000..523b97db5 --- /dev/null +++ b/Tests/Objective-J/Preprocessor/OutputTests/Misc/parenthesis-return-inlined.js @@ -0,0 +1,3 @@ +x=function(){ +return (someIval==null?null:(someIval.isa.method_msgSend["someMethod"]||_objj_forward)(someIval,"someMethod")); +}; diff --git a/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j b/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j index fcd96ba3c..8b19d2062 100644 --- a/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j +++ b/Tests/Objective-J/Preprocessor/OutputTests/OutputTest.j @@ -43,18 +43,28 @@ var FILENAMES = [ class_addMethod(self, testSelector, function(self, _cmd) { - var filePath = FILE.join(FILE.dirname(module.path), filename + ".j"), + var d = FILE.dirname(module.path), + filePath = FILE.join(d, filename + ".j"), unpreprocessed = FILE.read(filePath, { charset:"UTF-8" }), preprocessed, - correct = FILE.read(FILE.join(FILE.dirname(module.path), filename + ".js")); + preprocessedInlined, + correct = FILE.read(FILE.join(d, filename + ".js")), + p = FILE.join(d, filename + "-inlined.js"), + correctInlined = FILE.exists(p) ? FILE.read(p) : correct; // Get inlined version if it exists. Otherwise use the regular one. [self assertNoThrow:function() { - preprocessed = ObjectiveJ.ObjJAcornCompiler.compileToExecutable(unpreprocessed).code(); + preprocessed = ObjectiveJ.ObjJAcornCompiler.compileToExecutable(unpreprocessed, nil, ObjectiveJ.ObjJAcornCompiler.Flags.IncludeDebugSymbols/* | ObjectiveJ.ObjJAcornCompiler.Flags.IncludeTypeSignatures*/).code(); preprocessed = compressor.compress(preprocessed, { charset : "UTF-8", useServer : true }); correct = compressor.compress(correct, { charset : "UTF-8", useServer : true }); + + // Get an Inlined version + preprocessedInlined = ObjectiveJ.ObjJAcornCompiler.compileToExecutable(unpreprocessed, nil, ObjectiveJ.ObjJAcornCompiler.Flags.IncludeDebugSymbols | ObjectiveJ.ObjJAcornCompiler.Flags.InlineMsgSend/* | ObjectiveJ.ObjJAcornCompiler.Flags.IncludeTypeSignatures*/).code(); + preprocessedInlined = compressor.compress(preprocessedInlined, { charset : "UTF-8", useServer : true }); + correctInlined = compressor.compress(correctInlined, { charset : "UTF-8", useServer : true }); }]; [self assert:correct equals:preprocessed]; + [self assert:correctInlined equals:preprocessedInlined]; }); })(); } diff --git a/Tools/capp/Resources/Templates/Application/Jakefile b/Tools/capp/Resources/Templates/Application/Jakefile index 08a8c0c8b..a25464947 100644 --- a/Tools/capp/Resources/Templates/Application/Jakefile +++ b/Tools/capp/Resources/Templates/Application/Jakefile @@ -40,7 +40,7 @@ app (projectName, function(task) if (configuration === "Debug") task.setCompilerFlags("-DDEBUG -g"); else - task.setCompilerFlags("-O"); + task.setCompilerFlags("-O2"); }); task ("default", [projectName], function() diff --git a/Tools/capp/Resources/Templates/Application/index-debug.html b/Tools/capp/Resources/Templates/Application/index-debug.html index d862fd07e..91f96346c 100644 --- a/Tools/capp/Resources/Templates/Application/index-debug.html +++ b/Tools/capp/Resources/Templates/Application/index-debug.html @@ -33,6 +33,8 @@