diff --git a/AppKit/CPSound.j b/AppKit/CPSound.j index 701a1ca60..32f157da8 100644 --- a/AppKit/CPSound.j +++ b/AppKit/CPSound.j @@ -25,6 +25,7 @@ CPSoundLoadStateEmpty = 0; CPSoundLoadStateLoading = 1; CPSoundLoadStateCanBePlayed = 2; +CPSoundLoadStateError = 3; CPSoundPlayBackStatePlay = 0; CPSoundPlayBackStateStop = 1; @@ -60,14 +61,24 @@ CPSoundPlayBackStatePause = 2; _loops = NO; _audioTag = document.createElement("audio"); _audioTag.preload = YES; + _audioTag.addEventListener("canplay", function() { + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; [self _soundDidload]; - }); + }, true); + _audioTag.addEventListener("ended", function() { + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; [self _soundDidEnd]; - }); + }, true); + + _audioTag.addEventListener("error", function() + { + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + [self _soundError]; + }, true); } return self; @@ -137,31 +148,45 @@ CPSoundPlayBackStatePause = 2; [self stop]; } +/*! @ignore +*/ +- (void)_soundError +{ + _loadStatus = CPSoundLoadStateError; + CPLog.error("Cannot load sound. Maybe the format of your sound is not compatible with your browser."); +} + #pragma mark - #pragma mark Media controls /*! Play the sound. + + @return YES when the receiver is playing its audio data, NO otherwise. */ -- (void)play +- (BOOL)play { if ((_loadStatus !== CPSoundLoadStateCanBePlayed) || (_playBackStatus === CPSoundPlayBackStatePlay)) - return; + return NO; _audioTag.play(); _playBackStatus = CPSoundPlayBackStatePlay; + + return YES; } /*! Stop the sound. + + @return YES when the receiver is playing its audio data, NO otherwise. */ -- (void)stop +- (BOOL)stop { if ((_loadStatus !== CPSoundLoadStateCanBePlayed) || (_playBackStatus === CPSoundPlayBackStateStop)) - return; + return NO; _audioTag.pause(); _audioTag.currentTime = 0.0; @@ -169,32 +194,42 @@ CPSoundPlayBackStatePause = 2; if (_delegate && [_delegate respondsToSelector:@selector(sound:didFinishPlaying:)]) [_delegate sound:self didFinishPlaying:YES]; + + return YES; } /*! Pause the sound. + + @return YES when the receiver is playing its audio data, NO otherwise. */ -- (void)pause +- (BOOL)pause { if ((_loadStatus !== CPSoundLoadStateCanBePlayed) || (_playBackStatus === CPSoundPlayBackStatePause)) - return; + return NO; _audioTag.pause(); _playBackStatus = CPSoundPlayBackStatePause; + + return YES; } /*! Resume playback of a paused sound. + + @return YES when the receiver is playing its audio data, NO otherwise. */ -- (void)resume +- (BOOL)resume { if ((_loadStatus !== CPSoundLoadStateCanBePlayed) || (_playBackStatus !== CPSoundPlayBackStatePause)) - return; + return NO; _audioTag.play(); _playBackStatus = CPSoundPlayBackStatePlay; + + return YES; } /*! diff --git a/AppKit/CPView.j b/AppKit/CPView.j index afd79c850..74b800e34 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -2498,6 +2498,11 @@ setBoundsOrigin: return [_themeAttributes[aName] valueForState:_themeState]; } +- (BOOL)hasThemeAttribute:(CPString)aName +{ + return (_themeAttributes && _themeAttributes[aName] !== undefined); +} + - (CPView)createEphemeralSubviewNamed:(CPString)aViewName { return nil; diff --git a/AppKit/CoreGraphics/CGContextCanvas.j b/AppKit/CoreGraphics/CGContextCanvas.j index b13ac4c13..5a12aae01 100644 --- a/AppKit/CoreGraphics/CGContextCanvas.j +++ b/AppKit/CoreGraphics/CGContextCanvas.j @@ -240,7 +240,25 @@ function CGContextSetAlpha(aContext, anAlpha) function CGContextSetFillColor(aContext, aColor) { - aContext.fillStyle = [aColor cssString]; + if ([aColor patternImage]) + { + var patternImg = [aColor patternImage], + size = [patternImg size], + img; + + if (size) + img = new Image(size.width, size.height); + else + img = new Image(); + + img.src = [patternImg filename]; + + var pattern = aContext.createPattern(img, "repeat"); + + aContext.fillStyle = pattern; + } + else + aContext.fillStyle = [aColor cssString]; } function CGContextSetStrokeColor(aContext, aColor) diff --git a/AppKit/CoreGraphics/CGContextVML.j b/AppKit/CoreGraphics/CGContextVML.j index 0b45115fe..e3b09caf9 100644 --- a/AppKit/CoreGraphics/CGContextVML.j +++ b/AppKit/CoreGraphics/CGContextVML.j @@ -38,6 +38,15 @@ function CGBitmapGraphicsContextCreate() return _CGBitmapGraphicsContextCreate(); } +function CGContextSetFillColor(aContext, aColor) +{ + if ([aColor patternImage]) + // Prefix a marker character to the string so we know it's a pattern image filename + aContext.gState.fillStyle = "!" + [[aColor patternImage] filename]; + else + aContext.gState.fillStyle = [aColor cssString]; +} + // FIXME: aRect is ignored. function CGContextClearRect(aContext, aRect) { @@ -54,7 +63,7 @@ var W = 10.0, Z = 10.0, Z_2 = Z / 2.0; -#define COORD(aCoordinate) (ROUND(Z * (aCoordinate) - Z_2)) +#define COORD(aCoordinate) (aCoordinate === 0.0 ? 0 : ROUND(Z * (aCoordinate) - Z_2)) function CGContextDrawImage(aContext, aRect, anImage) { @@ -121,15 +130,18 @@ function CGContextDrawPath(aContext, aMode) fill = (aMode == kCGPathFill || aMode == kCGPathFillStroke) ? 1 : 0, stroke = (aMode == kCGPathStroke || aMode == kCGPathFillStroke) ? 1 : 0, opacity = gState.alpha, - vml = [ ""); + { + if (gState.fillStyle.charAt(0) === "!") + vml.push(""); + else // should be a CSS color spec + vml.push(""); + } if (stroke) vml.push( " + + +@implementation AppController : CPObject +{ + CPView contentView; + CPTextField label1; + CPTextField label2; + MyView myView; + CPImageView view2; + CPImage patternImage1; + BOOL patternImage1Loaded; + CPImage patternImage2; + BOOL patternImage2Loaded; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + var x = 20, + theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask]; + + contentView = [theWindow contentView]; + label1 = [CPTextField labelWithTitle:@"Dynamically drawn"]; + label2 = [CPTextField labelWithTitle:@"Screen capture from IE before COORD fix, notice the fuzziness in the pattern"]; + patternImage1 = [[CPImage alloc] initWithContentsOfFile:[[CPBundle mainBundle] pathForResource:@"help.png"] size:CGSizeMake(21, 22)]; + view2 = [[CPImageView alloc] initWithFrame:CGRectMake(x, 210, 200, 200)]; + img2 = [[CPImage alloc] initWithContentsOfFile:[[CPBundle mainBundle] pathForResource:@"bad.png"] size:CGSizeMake(200, 200)]; + patternImage2 = [[CPImage alloc] initWithContentsOfFile:[[CPBundle mainBundle] pathForResource:@"pattern.gif"] size:CGSizeMake(56, 32)]; + + var fillColor1 = [CPColor colorWithPatternImage:patternImage1], + fillColor2 = [CPColor colorWithPatternImage:patternImage2]; + + [view2 setImage:img2]; + + myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 200, 200) fillColor1:fillColor1 fillColor2:fillColor2]; + + [label1 setFrameOrigin:CGPointMake(x, 20)]; + [myView setFrameOrigin:CGPointMake(x, CGRectGetMaxY([label1 frame]) + 5)]; + + [label2 setFrameOrigin:CGPointMake(x, CGRectGetMaxY([myView frame]) + 20)]; + [view2 setFrameOrigin:CGPointMake(x, CGRectGetMaxY([label2 frame]) + 5)]; + + // Pattern images MUST be loaded before they are used, so we have to use a load delegate + // for each image and draw only when we have them all. + if ([patternImage1 loadStatus] === CPImageLoadStatusCompleted) + patternImage1Loaded = YES; + else + { + patternImage1Loaded = NO; + [patternImage1 setDelegate:self]; + } + + if ([patternImage2 loadStatus] === CPImageLoadStatusCompleted) + patternImage2Loaded = YES; + else + { + patternImage2Loaded = NO; + [patternImage2 setDelegate:self]; + } + + if (patternImage1Loaded && patternImage2Loaded) + [self addSubviews]; + + [theWindow orderFront:self]; + + // Uncomment the following line to turn on the standard menu bar. + //[CPMenu setMenuBarVisible:YES]; +} + +- (void)imageDidLoad:(CPImage)anImage +{ + if (anImage == patternImage1) + patternImage1Loaded = YES; + else if (anImage == patternImage2) + patternImage2Loaded = YES; + + if (patternImage1Loaded && patternImage2Loaded) + [self addSubviews]; +} + +- (void)addSubviews +{ + [contentView addSubview:label1]; + [contentView addSubview:label2]; + [contentView addSubview:myView]; + [contentView addSubview:view2]; +} + +@end + +@implementation MyView : CPView +{ + CPColor fillColor1; + CPColor fillColor2; +} + +- (id)initWithFrame:(CGRect)aFrame fillColor1:(CPColor)aColor1 fillColor2:(CPColor)aColor2 +{ + self = [super initWithFrame:aFrame]; + + if (self) + { + fillColor1 = aColor1; + fillColor2 = aColor2; + } + + return self; +} + +- (void)drawRect:(CGRect)dirtyRect +{ + var context = [[CPGraphicsContext currentContext] graphicsPort]; + + CGContextSetFillColor(context, fillColor1); + CGContextFillRect(context, [self bounds]); + + CGContextSetFillColor(context, [CPColor yellowColor]); + CGContextFillEllipseInRect(context, CGRectInset([self bounds], 40, 40)); + + CGContextSetStrokeColor(context, [CPColor blueColor]); + CGContextStrokeRect(context, CGRectInset([self bounds], 0.5, 0.5)); + + CGContextSetStrokeColor(context, [CPColor redColor]); + CGContextStrokeEllipseInRect(context, CGRectInset([self bounds], 20.5, 20.5)); + + CGContextSetStrokeColor(context, [CPColor greenColor]); + CGContextStrokeRect(context, CGRectInset([self bounds], 30.5, 30.5)); + + CGContextSetFillColor(context, fillColor2); + CGContextFillEllipseInRect(context, CGRectInset([self bounds], 50, 50)); +} + +@end diff --git a/Tests/Manual/PatternFillTest/Info.plist b/Tests/Manual/PatternFillTest/Info.plist new file mode 100644 index 000000000..3eb7a23af --- /dev/null +++ b/Tests/Manual/PatternFillTest/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + test + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/PatternFillTest/Jakefile b/Tests/Manual/PatternFillTest/Jakefile new file mode 100644 index 000000000..34daaa6db --- /dev/null +++ b/Tests/Manual/PatternFillTest/Jakefile @@ -0,0 +1,93 @@ +/* + * Jakefile + * test + * + * Created by aparajita on March 7, 2011. + * Copyright 2011, Victory-Heart Productions 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 ("test", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "test.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("test"); + task.setIdentifier("com.aparajitaworld.test"); + task.setVersion("1.0"); + task.setAuthor("Victory-Heart Productions"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("test"); + 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"); +}); + +task ("default", ["test"], 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", "test", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "test", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "test")); + OS.system(["press", "-f", FILE.join("Build", "Release", "test"), FILE.join("Build", "Deployment", "test")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "test")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "test"), FILE.join("Build", "Desktop", "test", "test.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "test", "test.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "test")); + print("----------------------------"); +} diff --git a/Tests/Manual/PatternFillTest/Resources/bad.png b/Tests/Manual/PatternFillTest/Resources/bad.png new file mode 100644 index 000000000..3f4d1a244 Binary files /dev/null and b/Tests/Manual/PatternFillTest/Resources/bad.png differ diff --git a/Tests/Manual/PatternFillTest/Resources/help.png b/Tests/Manual/PatternFillTest/Resources/help.png new file mode 100644 index 000000000..f7811563d Binary files /dev/null and b/Tests/Manual/PatternFillTest/Resources/help.png differ diff --git a/Tests/Manual/PatternFillTest/Resources/pattern.gif b/Tests/Manual/PatternFillTest/Resources/pattern.gif new file mode 100755 index 000000000..60f660904 Binary files /dev/null and b/Tests/Manual/PatternFillTest/Resources/pattern.gif differ diff --git a/Tests/Manual/PatternFillTest/Resources/spinner.gif b/Tests/Manual/PatternFillTest/Resources/spinner.gif new file mode 100644 index 000000000..06dbc2bc2 Binary files /dev/null and b/Tests/Manual/PatternFillTest/Resources/spinner.gif differ diff --git a/Tests/Manual/PatternFillTest/index-debug.html b/Tests/Manual/PatternFillTest/index-debug.html new file mode 100644 index 000000000..b5f2c8720 --- /dev/null +++ b/Tests/Manual/PatternFillTest/index-debug.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + test + + + + + + + + + + + + + + + + + + + + + + JavaScript is required for this site to work correctly but is either disabled or not supported by your browser. + Show me how to enable JavaScript + You may want to upgrade to a newer browser while you're at it: + + Safari + Firefox + Chrome + Opera + Internet Explorer + + + + + + + + + diff --git a/Tests/Manual/PatternFillTest/index.html b/Tests/Manual/PatternFillTest/index.html new file mode 100644 index 000000000..103eb5bc1 --- /dev/null +++ b/Tests/Manual/PatternFillTest/index.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + test + + + + + + + + + + + + + + + + + + + + JavaScript is required for this site to work correctly but is either disabled or not supported by your browser. + Show me how to enable JavaScript + You may want to upgrade to a newer browser while you're at it: + + Safari + Firefox + Chrome + Opera + Internet Explorer + + + + + + + + + diff --git a/Tests/Manual/PatternFillTest/main.j b/Tests/Manual/PatternFillTest/main.j new file mode 100644 index 000000000..3fcfef3ec --- /dev/null +++ b/Tests/Manual/PatternFillTest/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * test + * + * Created by aparajita on March 7, 2011. + * Copyright 2011, Victory-Heart Productions All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +} diff --git a/Tools/capp/Resources/Templates/Application/index-debug.html b/Tools/capp/Resources/Templates/Application/index-debug.html index 33b76c2a7..871c9f72d 100644 --- a/Tools/capp/Resources/Templates/Application/index-debug.html +++ b/Tools/capp/Resources/Templates/Application/index-debug.html @@ -26,9 +26,9 @@ OBJJ_MAIN_FILE = "main.j"; OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks", "SomethingElse"]; - + - + - +
JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.
Show me how to enable JavaScript
You may want to upgrade to a newer browser while you're at it: