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 + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + 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 + + + + + + + + + + + + +
+
+ + + +
+
+ + + 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"]; - + - + - +