Don't load Checkers sound files till a game is displayed on a board; this speeds up launch.
5 // Created by Jens Alfke on 3/7/08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
10 #import "QuartzUtils.h"
14 NSString* const GGBLayerStyleChangedNotification = @"GGBLayerStyleChanged";
17 @implementation GGBLayer
20 - (NSString*) description
22 return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
28 [self setNeedsDisplay];
29 for( CALayer *layer in self.sublayers )
30 if( [layer isKindOfClass: [GGBLayer class]] )
31 ((GGBLayer*)layer).redisplayAll;
33 [layer setNeedsDisplay];
38 - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
40 NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key);
41 [super addAnimation: anim forKey: key];
46 - (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration
48 //WARNING: This code works, but is a mess. I hope to find a better way to do this. --Jens 3/16/08
49 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath: keyPath];
50 anim.duration= duration;
51 anim.fromValue = from;
53 anim.isRemovedOnCompletion = YES;
55 [self addAnimation:anim forKey: @"animateAndBlock:"];
56 _curAnimation = (id)[self animationForKey: @"animateAndBlock:"];
57 [self setValue: to forKeyPath: keyPath]; // animation doesn't update the property value
59 if( self.presentationLayer ) {
60 // Now wait for it to finish:
61 while( _curAnimation ) {
62 [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode
63 beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
70 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
72 if( anim==_curAnimation ) {
78 - (void) setStyle: (NSDictionary*)style
80 if( style != _styleDict ) {
82 [[NSNotificationCenter defaultCenter] removeObserver: self
83 name: GGBLayerStyleChangedNotification
86 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_styleChanged)
87 name: GGBLayerStyleChangedNotification
89 setObj(&_styleDict,style);
91 [super setStyle: style];
94 - (void) _styleChanged
96 // Reapply the style, so any changes in the dict will take effect.
97 [super setStyle: _styleDict];
103 [[NSNotificationCenter defaultCenter] removeObserver: self
104 name: GGBLayerStyleChangedNotification
110 - (void) setValue: (id)value ofStyleProperty: (NSString*)prop
113 id oldValue = [_styleDict objectForKey: prop];
114 if( oldValue != value ) {
116 [_styleDict setObject: value forKey: prop];
118 [_styleDict removeObjectForKey: prop];
119 [[NSNotificationCenter defaultCenter] postNotificationName: GGBLayerStyleChangedNotification
123 [self setValue: value forKey: prop];
128 - (CATransform3D) aggregateTransform
130 CATransform3D xform = CATransform3DIdentity;
131 for( CALayer *layer=self; layer; layer=layer.superlayer ) {
132 xform = CATransform3DConcat(layer.transform,xform);
133 xform = CATransform3DConcat(layer.sublayerTransform,xform);
139 NSString* StringFromTransform3D( CATransform3D xform )
141 NSMutableString *str = [NSMutableString string];
142 const CGFloat *np = (const CGFloat*)&xform;
143 for( int i=0; i<16; i++ ) {
144 if( i>0 && (i%4)==0 )
145 [str appendString: @"\n"];
146 [str appendFormat: @"%7.2f ", *np++];
156 #pragma mark IPHONE VERSION:
159 - (id) copyWithZone: (NSZone*)zone
161 GGBLayer *clone = [[[self class] alloc] init];
162 clone.bounds = self.bounds;
163 clone.position = self.position;
164 clone.zPosition = self.zPosition;
165 clone.anchorPoint = self.anchorPoint;
166 clone.transform = self.transform;
167 clone.hidden = self.hidden;
168 clone.doubleSided = self.doubleSided;
169 clone.sublayerTransform = self.sublayerTransform;
170 clone.masksToBounds = self.masksToBounds;
171 clone.contents = self.contents; // doesn't copy contents (shallow-copy)
172 clone.contentsRect = self.contentsRect;
173 clone.contentsGravity = self.contentsGravity;
174 clone.minificationFilter = self.minificationFilter;
175 clone.magnificationFilter = self.magnificationFilter;
176 clone.opaque = self.opaque;
177 clone.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange;
178 clone.edgeAntialiasingMask = self.edgeAntialiasingMask;
179 clone.backgroundColor = self.backgroundColor;
180 clone.opacity = self.opacity;
181 clone.compositingFilter = self.compositingFilter;
182 clone.filters = self.filters;
183 clone.backgroundFilters = self.backgroundFilters;
184 clone.actions = self.actions;
185 clone.name = self.name;
186 clone.style = self.style;
188 clone.cornerRadius = self.cornerRadius;
189 clone.borderWidth = self.borderWidth;
190 clone.borderColor = self.borderColor;
192 for( GGBLayer *sublayer in self.sublayers ) {
193 sublayer = [sublayer copyWithZone: zone];
194 [clone addSublayer: sublayer];
200 - (CGFloat) cornerRadius {return _cornerRadius;}
201 - (CGFloat) borderWidth {return _borderWidth;}
202 - (CGColorRef) backgroundColor {return _realBGColor;}
203 - (CGColorRef) borderColor {return _borderColor;}
205 - (void) setCornerRadius: (CGFloat)r
207 if( r != _cornerRadius ) {
209 [self setNeedsDisplay];
214 - (void) setBorderWidth: (CGFloat)w
216 if( w != _borderWidth ) {
218 self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
219 [self setNeedsDisplay];
224 - (void) setBackgroundColor: (CGColorRef)color
226 if( color != _realBGColor ) {
227 CGColorRelease(_realBGColor);
228 _realBGColor = CGColorRetain(color);
229 [self setNeedsDisplay];
234 - (void) setBorderColor: (CGColorRef)color
236 if( color != _borderColor ) {
237 CGColorRelease(_borderColor);
238 _borderColor = CGColorRetain(color);
239 self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
240 [self setNeedsDisplay];
245 - (void)drawInContext:(CGContextRef)ctx
247 [super drawInContext: ctx];
249 CGContextSaveGState(ctx);
252 CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth);
253 CGContextSetFillColorWithColor(ctx, _realBGColor);
254 if( _cornerRadius <= 0.0 ) {
255 CGContextFillRect(ctx,interior);
257 CGContextBeginPath(ctx);
258 AddRoundRect(ctx,interior,_cornerRadius);
259 CGContextFillPath(ctx);
263 if( _borderWidth > 0.0 && _borderColor!=NULL ) {
264 CGRect border = CGRectInset(self.bounds, _borderWidth/2.0, _borderWidth/2.0);
265 CGContextSetStrokeColorWithColor(ctx, _borderColor);
266 CGContextSetLineWidth(ctx, _borderWidth);
268 if( _cornerRadius <= 0.0 ) {
269 CGContextStrokeRect(ctx,border);
271 CGContextBeginPath(ctx);
272 AddRoundRect(ctx,border,_cornerRadius);
273 CGContextStrokePath(ctx);
277 CGContextRestoreGState(ctx);
284 #pragma mark MAC OS VERSION:
287 - (id) copyWithZone: (NSZone*)zone
289 // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to
290 // a temporary data block, then unarchiving a new layer from that block.
292 // One complication is that, due to a bug in Core Animation, CALayer can't archive
293 // a pattern-based CGColor. So as a workaround, clear the background before archiving,
294 // then restore it afterwards.
296 // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 )
297 // As a workaround, clear the contents before archiving, then restore.
299 CGColorRef bg = CGColorRetain(self.backgroundColor);
300 self.backgroundColor = NULL;
301 id contents = [self.contents retain];
304 NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
306 self.backgroundColor = bg;
307 self.contents = contents;
309 GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
310 clone.backgroundColor = bg;
311 clone.contents = contents;
315 return [clone retain];
327 #pragma mark UTILITIES:
330 void BeginDisableAnimations(void)
332 [CATransaction begin];
333 [CATransaction setValue:(id)kCFBooleanTrue
334 forKey:kCATransactionDisableActions];
337 void EndDisableAnimations(void)
339 [CATransaction commit];
343 void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
345 // Disable actions, else the layer will move to the wrong place and then back!
346 [CATransaction flush];
347 BeginDisableAnimations();
349 CGPoint pos = layer.position;
350 if( layer.superlayer )
351 pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer];
353 [layer removeFromSuperlayer];
354 layer.position = pos;
356 [newSuperlayer insertSublayer: layer atIndex: index];
358 [newSuperlayer addSublayer: layer];
361 EndDisableAnimations();
365 void RemoveImmediately( CALayer *layer )
367 [CATransaction flush];
368 BeginDisableAnimations();
369 [layer removeFromSuperlayer];
370 EndDisableAnimations();
374 CGColorRef GetEffectiveBackground( CALayer *layer )
376 for( ; layer; layer=layer.superlayer ) {
377 CGColorRef bg = layer.backgroundColor;