diff --git a/AppKit/CPButtonBar.j b/AppKit/CPButtonBar.j index 1bdbc484e..5d883ddce 100644 --- a/AppKit/CPButtonBar.j +++ b/AppKit/CPButtonBar.j @@ -69,6 +69,7 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; @implementation CPButtonBar : CPView { CPArray _buttons; + CPArray _rightButtons; CPMutableArray _dividers; CPMutableArray _flexibles; CPButtonBarStyle _style; @@ -98,6 +99,8 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; id _delegate; unsigned _delegateSelectors; + BOOL _hasResizeControl; + BOOL _resizeControlIsLeftAligned; } #pragma mark - @@ -288,6 +291,7 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; if (self) { _buttons = @[]; + _rightButtons = @[]; _dividers = @[]; _flexibles = @[]; _flexibleSpacesCount = 0; @@ -302,7 +306,7 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; [self setButtonsAreBordered: [self currentValueForThemeAttribute:@"bordered-buttons"]]; [self setDrawsSeparator: [self currentValueForThemeAttribute:@"draws-separator"]]; [self setIsTransparent: [self currentValueForThemeAttribute:@"is-transparent"]]; - [self setAutomaticResizeControl:[self currentValueForThemeAttribute:@"auto-resize-control"]] + [self setAutomaticResizeControl:[self currentValueForThemeAttribute:@"auto-resize-control"]]; // Adapt the height of the button bar if needed var minSize = [self currentValueForThemeAttribute:@"min-size"], @@ -323,7 +327,7 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; // This is needed when compiling themes [self _loadThemeValues]; - [self setNeedsLayout:YES]; + [self setNeedsLayout]; } return self; @@ -544,6 +548,30 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; return [CPArray arrayWithArray:_buttons]; } +- (void)setRightButtons:(CPArray)buttons +{ + for (var i = [_rightButtons count] - 1; i >= 0; i--) + { + [_rightButtons[i] removeFromSuperview]; + [_rightButtons[i] removeObserver:self forKeyPath:@"hidden"]; + } + + _rightButtons = [CPArray arrayWithArray:buttons]; + + for (var i = [_rightButtons count] - 1; i >= 0; i--) + { + [_rightButtons[i] addObserver:self forKeyPath:@"hidden" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:nil]; + [_rightButtons[i] setBordered:YES]; + } + + [self setNeedsLayout]; +} + +- (CPArray)rightButtons +{ + return [CPArray arrayWithArray:_rightButtons]; +} + - (void)setButtonsAreBordered:(BOOL)shouldBeBordered { if (_buttonsAreBordered === shouldBeBordered) @@ -881,6 +909,7 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; flexibleSpaceWidth = (remainingSpace > 0) ? remainingSpace / _flexibleSpacesCount : 0; } + // --- 1. LINKE/NORMALE BUTTONS PLATZIEREN --- for (var i = 0, count = _buttons.length, button, width, extraSpace, buttonHeight; i < count; i++) { button = _buttons[i]; @@ -917,10 +946,41 @@ var CPButtonBarPopulateButtonBarSelector = 1 << 1; } } + // --- 2. RECHTE BUTTONS (aus Upstream) PLATZIEREN --- + var rightButtonsNotHidden = [CPArray arrayWithArray:_rightButtons], + rightCount = [rightButtonsNotHidden count]; + + while (rightCount--) + { + var button = rightButtonsNotHidden[rightCount]; + + if ([button isHidden]) + { + [button removeFromSuperview]; + [rightButtonsNotHidden removeObject:button]; + } + } + + var currentRightOffset = _hasRightResizeControl ? CGRectGetMaxX(bounds) - rightResizeWidth + 1 : CGRectGetMaxX(bounds) + 1; + + for (var i = 0, count = [rightButtonsNotHidden count]; i < count; i++) + { + var rightButton = rightButtonsNotHidden[i], + btnWidth = CGRectGetWidth([rightButton frame]), + btnHeight = CGRectGetHeight([rightButton frame]); + + [rightButton setFrame:CGRectMake(currentRightOffset - btnWidth, _buttonVerticalOffset + (height - btnHeight)/2, btnWidth, btnHeight)]; + currentRightOffset -= btnWidth - 1; + + if (![rightButton superview]) + [self addSubview:rightButton]; + } + + // --- 3. RESIZE CONTROLS --- if (_hasLeftResizeControl) { var resizeControlView = [self layoutEphemeralSubviewNamed:@"left-resize-control-view" - positioned:CPWindowAbove + positioned:CPWindowAbove relativeToEphemeralSubviewNamed:nil]; [resizeControlView setAutoresizingMask:CPViewMaxXMargin]; @@ -993,7 +1053,10 @@ var CPButtonBarHasLeftResizeControlKey = @"CPButtonBarHasLeftResizeControl CPButtonBarDividersKey = @"CPButtonBarDividersKey", CPButtonBarFlexiblesKey = @"CPButtonBarFlexiblesKey", CPButtonBarFlexibleSpacesCountKey = @"CPButtonBarFlexibleSpacesCountKey", - CPButtonBarDelegateKey = @"CPButtonBarDelegateKey"; + CPButtonBarDelegateKey = @"CPButtonBarDelegateKey", + CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey", + CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey", + CPButtonBarRightButtonsKey = @"CPButtonBarRightButtonsKey"; @implementation CPButtonBar (CPCoding) @@ -1011,27 +1074,32 @@ var CPButtonBarHasLeftResizeControlKey = @"CPButtonBarHasLeftResizeControl [aCoder encodeObject:_dividers forKey:CPButtonBarDividersKey]; [aCoder encodeObject:_flexibles forKey:CPButtonBarFlexiblesKey]; [aCoder encodeInt: _flexibleSpacesCount forKey:CPButtonBarFlexibleSpacesCountKey]; - [aCoder encodeConditionalObject:_delegate forKey:CPButtonBarDelegateKey]; + [aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey]; + [aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey]; + [aCoder encodeObject:_rightButtons forKey:CPButtonBarRightButtonsKey]; } - (id)initWithCoder:(CPCoder)aCoder { if (self = [super initWithCoder:aCoder]) { - _hasLeftResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasLeftResizeControlKey]; - _hasRightResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasRightResizeControlKey]; - _automaticResizeControl = [aCoder decodeBoolForKey:CPButtonBarAutomaticResizeControlKey]; + _hasLeftResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasLeftResizeControlKey]; + _hasRightResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasRightResizeControlKey]; + _automaticResizeControl = [aCoder decodeBoolForKey:CPButtonBarAutomaticResizeControlKey]; + _hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey]; + _resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey]; [self setButtonsAreBordered:!![aCoder decodeBoolForKey:CPButtonBarButtonsAreBorderedKey]]; [self setDrawsSeparator: !![aCoder decodeBoolForKey:CPButtonBarDrawsSeparatorKey]]; [self setIsTransparent: !![aCoder decodeBoolForKey:CPButtonBarIsTransparentKey]]; _buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || @[]; + _rightButtons = [aCoder decodeObjectForKey:CPButtonBarRightButtonsKey] || []; _dividers = [aCoder decodeObjectForKey:CPButtonBarDividersKey] || @[]; _flexibles = [aCoder decodeObjectForKey:CPButtonBarFlexiblesKey] || @[]; _flexibleSpacesCount = [aCoder decodeIntForKey:CPButtonBarFlexibleSpacesCountKey]; - + [self setNeedsRelayout:YES]; } diff --git a/AppKit/CPRuleEditor/CPPredicateEditor.j b/AppKit/CPRuleEditor/CPPredicateEditor.j index 16cf1f685..5076b7ddc 100644 --- a/AppKit/CPRuleEditor/CPPredicateEditor.j +++ b/AppKit/CPRuleEditor/CPPredicateEditor.j @@ -269,6 +269,8 @@ _currentAnimation = nil; _sendAction = NO; + var rows = []; + if (predicate != nil) { if ((_nestingMode == CPRuleEditorNestingModeSimple || _nestingMode == CPRuleEditorNestingModeCompound) @@ -276,10 +278,13 @@ predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:[CPArray arrayWithObject:predicate]]; var row = [self _rowObjectFromPredicate:predicate]; + if (row != nil) - [_boundArrayOwner setValue:[CPArray arrayWithObject:row] forKey:_boundArrayKeyPath]; + [rows addObject:row]; } + [_boundArrayOwner setValue:rows forKey:_boundArrayKeyPath]; + [self setAnimation:animation]; } diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j index f789fcbf9..855342d6a 100644 --- a/Foundation/CPKeyValueObserving.j +++ b/Foundation/CPKeyValueObserving.j @@ -763,9 +763,12 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti if (!anObserver) return; - var forwarder = nil; + var forwarder = nil, + collectionOperatorMatch = aPath.match(/^(.*)\.(@\w+)(?:\.(.*))?$/); - if (aPath.indexOf('.') !== CPNotFound && aPath.charAt(0) !== '@') + if (collectionOperatorMatch) + forwarder = [[_CPKVOCollectionOperatorObserver alloc] initWithTarget:_targetObject observer:anObserver keyPath:aPath options:options context:aContext]; + else if (aPath.indexOf('.') !== CPNotFound && aPath.charAt(0) !== '@') forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext]; else [self _replaceModifiersForKey:aPath]; @@ -1265,6 +1268,193 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti @end + +@implementation _CPKVOCollectionOperatorObserver : CPObject +{ + id _target; + id _originalObserver; + CPString _fullKeyPath; + unsigned _options; + id _context; + + CPString _collectionKeyPath; + CPString _operator; + CPString _valueKeyPath; + + CPArray _observedItems; + id _cachedValue; +} + +- (id)initWithTarget:(id)aTarget observer:(id)anObserver keyPath:(CPString)aKeyPath options:(unsigned)options context:(id)aContext +{ + self = [super init]; + + if (self) + { + _target = aTarget; + _originalObserver = anObserver; + _fullKeyPath = aKeyPath; + _options = options; + _context = aContext; + _observedItems = [CPArray array]; + + var match = aKeyPath.match(/^(.*)\.(@\w+)(?:\.(.*))?$/); + if (match) + { + _collectionKeyPath = match[1]; + _operator = match[2]; + _valueKeyPath = match[3]; + } + + _cachedValue = [_target valueForKeyPath:_fullKeyPath]; + + [_target addObserver:self forKeyPath:_collectionKeyPath options:_options context:nil]; + + [self _setupItemObservers]; + } + + return self; +} + +- (void)_setupItemObservers +{ + var collection = [_target valueForKeyPath:_collectionKeyPath]; + + if (collection && [collection respondsToSelector:@selector(objectEnumerator)]) + { + var enumerator = [collection objectEnumerator], + item; + + while ((item = [enumerator nextObject]) !== nil) + { + if (_valueKeyPath && [item respondsToSelector:@selector(addObserver:forKeyPath:options:context:)]) + [item addObserver:self forKeyPath:_valueKeyPath options:_options context:nil]; + + [_observedItems addObject:item]; + } + } +} + +- (void)_tearDownItemObservers +{ + if (_valueKeyPath) + { + var count = [_observedItems count]; + while (count--) + { + var item = [_observedItems objectAtIndex:count]; + if ([item respondsToSelector:@selector(removeObserver:forKeyPath:context:)]) + [item removeObserver:self forKeyPath:_valueKeyPath context:nil]; + } + } + + [_observedItems removeAllObjects]; +} + +- (void)observeValueForKeyPath:(CPString)keyPath ofObject:(id)object change:(CPDictionary)change context:(id)context +{ + var isPrior = [[change objectForKey:CPKeyValueChangeNotificationIsPriorKey] boolValue]; + + if (object === _target && [keyPath isEqualToString:_collectionKeyPath]) + { + // Only tear down/rebuild observers when the actual change happens, not during the "Prior" notification + if (!isPrior) + { + var kind = [change objectForKey:CPKeyValueChangeKindKey]; + + if (kind === CPKeyValueChangeSetting) + { + [self _tearDownItemObservers]; + [self _setupItemObservers]; + } + else if (kind === CPKeyValueChangeInsertion || kind === CPKeyValueChangeReplacement) + { + if (kind === CPKeyValueChangeReplacement) + { + var oldItems = [change objectForKey:CPKeyValueChangeOldKey]; + if (oldItems) + { + for (var i = 0, len = oldItems.length; i < len; i++) + { + var item = oldItems[i]; + if (_valueKeyPath && [item respondsToSelector:@selector(removeObserver:forKeyPath:context:)]) + [item removeObserver:self forKeyPath:_valueKeyPath context:nil]; + [_observedItems removeObject:item]; + } + } + } + + var newItems = [change objectForKey:CPKeyValueChangeNewKey]; + if (newItems) + { + for (var i = 0, len = newItems.length; i < len; i++) + { + var item = newItems[i]; + if (_valueKeyPath && [item respondsToSelector:@selector(addObserver:forKeyPath:options:context:)]) + [item addObserver:self forKeyPath:_valueKeyPath options:_options context:nil]; + [_observedItems addObject:item]; + } + } + } + else if (kind === CPKeyValueChangeRemoval) + { + var oldItems = [change objectForKey:CPKeyValueChangeOldKey]; + if (oldItems) + { + for (var i = 0, len = oldItems.length; i < len; i++) + { + var item = oldItems[i]; + if (_valueKeyPath && [item respondsToSelector:@selector(removeObserver:forKeyPath:context:)]) + [item removeObserver:self forKeyPath:_valueKeyPath context:nil]; + [_observedItems removeObject:item]; + } + } + } + } + + [self _notifyOriginalObserverIsPrior:isPrior]; + } + else if (_valueKeyPath && [keyPath isEqualToString:_valueKeyPath] && [_observedItems containsObject:object]) + { + [self _notifyOriginalObserverIsPrior:isPrior]; + } +} + +- (void)_notifyOriginalObserverIsPrior:(BOOL)isPrior +{ + var change = [CPMutableDictionary dictionaryWithObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey]; + + if (isPrior) + [change setObject:YES forKey:CPKeyValueChangeNotificationIsPriorKey]; + + if (_options & CPKeyValueObservingOptionOld) + [change setObject:(_cachedValue !== nil ? _cachedValue : [CPNull null]) forKey:CPKeyValueChangeOldKey]; + + if (!isPrior) + { + var newValue = [_target valueForKeyPath:_fullKeyPath]; + if (_options & CPKeyValueObservingOptionNew) + [change setObject:(newValue !== nil ? newValue : [CPNull null]) forKey:CPKeyValueChangeNewKey]; + + [_originalObserver observeValueForKeyPath:_fullKeyPath ofObject:_target change:change context:_context]; + + // Update the cache for the next notification + _cachedValue = newValue; + } + else + { + [_originalObserver observeValueForKeyPath:_fullKeyPath ofObject:_target change:change context:_context]; + } +} + +- (void)finalize +{ + [_target removeObserver:self forKeyPath:_collectionKeyPath]; + [self _tearDownItemObservers]; +} + +@end + @implementation _CPKVOForwardingObserver : CPObject { id _object; diff --git a/Tests/Foundation/CPKVOTest.j b/Tests/Foundation/CPKVOTest.j index 631e2ac80..b3215ec4e 100644 --- a/Tests/Foundation/CPKVOTest.j +++ b/Tests/Foundation/CPKVOTest.j @@ -3,6 +3,8 @@ @class CarTester @class ToManyTester +@class AccountTester +@class TransactionTester @implementation CPKVOTest : OJTestCase { @@ -436,6 +438,35 @@ [self assertTrue:_sawObservation message:@"Never recieved an observation"]; } +- (void)testCollectionOperatorKeyPath +{ + var account = [[AccountTester alloc] init], + t1 = [[TransactionTester alloc] initWithAmount:10.0], + t2 = [[TransactionTester alloc] initWithAmount:20.0]; + + [account setTransactions:[t1, t2]]; + + // 1. Test updating a child property triggers an aggregate update + [account addObserver:self forKeyPath:@"transactions.@sum.amount" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:@"testCollectionOperatorKeyPath_ItemChange"]; + + _sawObservation = NO; + [t1 setAmount:15.0]; // KVC sum goes from 30.0 -> 35.0 + + [self assertTrue:_sawObservation message:@"Never received an observation when child item's property changed"]; + [account removeObserver:self forKeyPath:@"transactions.@sum.amount"]; + + // 2. Test replacing the whole array triggers an aggregate update + [account addObserver:self forKeyPath:@"transactions.@sum.amount" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:@"testCollectionOperatorKeyPath_ArrayReplace"]; + + var t3 = [[TransactionTester alloc] initWithAmount:5.0]; + + _sawObservation = NO; + [account setTransactions:[t1, t2, t3]]; // KVC sum goes from 35.0 -> 40.0 + + [self assertTrue:_sawObservation message:@"Never received an observation when collection was replaced"]; + [account removeObserver:self forKeyPath:@"transactions.@sum.amount"]; +} + - (void)testPerformance { bob = [PersonTester new]; @@ -777,6 +808,16 @@ testNestedNotificationsBobCount += 1; break; + case "testCollectionOperatorKeyPath_ItemChange": + [self assert:newValue equals:35.0]; + [self assert:oldValue equals:30.0]; + break; + + case "testCollectionOperatorKeyPath_ArrayReplace": + [self assert:newValue equals:40.0]; + [self assert:oldValue equals:35.0]; + break; + default: [self assertFalse:YES message:@"unhandled observation, must be an error"]; return; @@ -955,3 +996,25 @@ } @end + +@implementation TransactionTester : CPObject +{ + float amount @accessors; +} + +- (id)initWithAmount:(float)anAmount +{ + self = [super init]; + if (self) { + amount = anAmount; + } + return self; +} + +@end + +@implementation AccountTester : CPObject +{ + CPArray transactions @accessors; +} +@end diff --git a/Tests/Manual/CPButtonBarTest/AppController.j b/Tests/Manual/CPButtonBarTest/AppController.j new file mode 100644 index 000000000..20e1af7ae --- /dev/null +++ b/Tests/Manual/CPButtonBarTest/AppController.j @@ -0,0 +1,104 @@ +/* + * AppController.j + * CPButtonBar Right Buttons Manual Test + */ + +@import +@import +@import +@import + +@implementation AppController : CPObject +{ + CPButtonBar buttonBar; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; + + var bounds = [contentView bounds]; + + // 1. Create the ButtonBar + buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(bounds) - 28, CGRectGetWidth(bounds), 28)]; + [buttonBar setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin]; + + // Add resize control to ensure bounds calculations are correct + [buttonBar setHasResizeControl:YES]; + + // 2. Setup standard Left Buttons + var plusButton = [CPButtonBar plusButton], + minusButton = [CPButtonBar minusButton]; + + [buttonBar setButtons:[plusButton, minusButton]]; + + // 3. Setup the new Right Buttons (PR #745) + var actionButton = [CPButtonBar actionPopupButton], + customRightBtn = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 70, 24)]; + + [customRightBtn setTitle:@"Settings"]; + [buttonBar setRightButtons:[customRightBtn, actionButton]]; + + [contentView addSubview:buttonBar]; + + // --- Testing Controls --- + + // Button to test resizing alignment layout + var toggleAlignmentBtn = [CPButton buttonWithTitle:@"Toggle Resize Control Alignment"]; + [toggleAlignmentBtn setFrameOrigin:CGPointMake(20, 20)]; + [toggleAlignmentBtn sizeToFit]; + [toggleAlignmentBtn setTarget:self]; + [toggleAlignmentBtn setAction:@selector(toggleResizeAlignment:)]; + [contentView addSubview:toggleAlignmentBtn]; + + // Button to test the KVO "hidden" observer on rightButtons + var toggleVisibilityBtn = [CPButton buttonWithTitle:@"Toggle Right Button Visibility"]; + [toggleVisibilityBtn setFrameOrigin:CGPointMake(20, 60)]; + [toggleVisibilityBtn sizeToFit]; + [toggleVisibilityBtn setTarget:self]; + [toggleVisibilityBtn setAction:@selector(toggleRightVisibility:)]; + [contentView addSubview:toggleVisibilityBtn]; + + // Button to test CPCoding archiving/unarchiving of rightButtons + var testArchivingBtn = [CPButton buttonWithTitle:@"Test Archiving (CPCoding)"]; + [testArchivingBtn setFrameOrigin:CGPointMake(20, 100)]; + [testArchivingBtn sizeToFit]; + [testArchivingBtn setTarget:self]; + [testArchivingBtn setAction:@selector(testArchiving:)]; + [contentView addSubview:testArchivingBtn]; + + [theWindow orderFront:self]; +} + +- (void)toggleResizeAlignment:(id)sender +{ + // Flipping the resize control to the left shouldn't break the right-aligned buttons + [buttonBar setResizeControlIsLeftAligned:![buttonBar resizeControlIsLeftAligned]]; +} + +- (void)toggleRightVisibility:(id)sender +{ + var rightBtns = [buttonBar rightButtons]; + if ([rightBtns count] > 0) + { + var btn = rightBtns[0]; + [btn setHidden:![btn isHidden]]; + } +} + +- (void)testArchiving:(id)sender +{ + // Test the new CPButtonBarRightButtonsKey in CPCoding + var data = [CPKeyedArchiver archivedDataWithRootObject:buttonBar]; + var unarchivedBar = [CPKeyedUnarchiver unarchiveObjectWithData:data]; + + var bounds = [[buttonBar superview] bounds]; + + // Place the cloned bar directly above the original one + [unarchivedBar setFrame:CGRectMake(0, CGRectGetHeight(bounds) - 56, CGRectGetWidth(bounds), 28)]; + + [[buttonBar superview] addSubview:unarchivedBar]; +} + +@end diff --git a/Tests/Manual/CPButtonBarTest/Info.plist b/Tests/Manual/CPButtonBarTest/Info.plist new file mode 100644 index 000000000..e9a691d12 --- /dev/null +++ b/Tests/Manual/CPButtonBarTest/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + CPButtonBarTest + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/CPButtonBarTest/Jakefile b/Tests/Manual/CPButtonBarTest/Jakefile new file mode 100644 index 000000000..542de14fd --- /dev/null +++ b/Tests/Manual/CPButtonBarTest/Jakefile @@ -0,0 +1,81 @@ +/* + * Jakefile + * CPBrowserTest + * + * Created by Ross Boucher on March 23, 2010. + * Copyright 2010, Your Company All rights reserved. + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"); + +app ("CPBrowserTest", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "CPBrowserTest.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("CPBrowserTest"); + task.setIdentifier("com.yourcompany.CPBrowserTest"); + task.setVersion("1.0"); + task.setAuthor("Your Company"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("CPBrowserTest"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/*")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "CPBrowserTest")); + print("----------------------------"); +} + +task ("default", ["CPBrowserTest"], function() +{ + printResults(configuration); +}); + +task ("build", ["default"]); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", "CPBrowserTest", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "CPBrowserTest", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "CPBrowserTest")); + OS.system(["press", "-f", FILE.join("Build", "Release", "CPBrowserTest"), FILE.join("Build", "Deployment", "CPBrowserTest")]); + printResults("Deployment") +}); diff --git a/Tests/Manual/CPButtonBarTest/Resources/spinner.gif b/Tests/Manual/CPButtonBarTest/Resources/spinner.gif new file mode 100644 index 000000000..a5e705f6c Binary files /dev/null and b/Tests/Manual/CPButtonBarTest/Resources/spinner.gif differ diff --git a/Tests/Manual/CPButtonBarTest/index-debug.html b/Tests/Manual/CPButtonBarTest/index-debug.html new file mode 100644 index 000000000..a36b1d3b9 --- /dev/null +++ b/Tests/Manual/CPButtonBarTest/index-debug.html @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + __project.name__ + + + + + + + + + + + + + + +
+
+
+ +
+
+ +
+ + diff --git a/Tests/Manual/CPButtonBarTest/index.html b/Tests/Manual/CPButtonBarTest/index.html new file mode 100644 index 000000000..ac42c98a7 --- /dev/null +++ b/Tests/Manual/CPButtonBarTest/index.html @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + __project.name__ + + + + + + + + + + + + +
+
+
+ +
+
+ +
+ + diff --git a/Tests/Manual/CPButtonBarTest/main.j b/Tests/Manual/CPButtonBarTest/main.j new file mode 100644 index 000000000..b953537a0 --- /dev/null +++ b/Tests/Manual/CPButtonBarTest/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * CPButtonBarTest + * + * Created by Ross Boucher on March 23, 2010. + * Copyright 2010, Your Company All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +}