Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
5 // Created by Jens Alfke on 3/7/08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
10 #import "QuartzUtils.h"
13 @implementation GGBLayer
16 - (NSString*) description
18 return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
24 [self setNeedsDisplay];
25 for( CALayer *layer in self.sublayers )
26 if( [layer isKindOfClass: [GGBLayer class]] )
27 ((GGBLayer*)layer).redisplayAll;
29 [layer setNeedsDisplay];
34 - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
36 NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key);
37 [super addAnimation: anim forKey: key];
42 - (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration
44 //WARNING: This code works, but is a mess. I hope to find a better way to do this. --Jens 3/16/08
45 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath: keyPath];
46 anim.duration= duration;
47 anim.fromValue = from;
49 anim.isRemovedOnCompletion = YES;
51 [self addAnimation:anim forKey: @"animateAndBlock:"];
52 _curAnimation = (id)[self animationForKey: @"animateAndBlock:"];
53 [self setValue: to forKeyPath: keyPath]; // animation doesn't update the property value
55 // Now wait for it to finish:
56 while( _curAnimation ) {
57 [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode
58 beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
62 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
64 if( anim==_curAnimation ) {
73 #pragma mark IPHONE VERSION:
76 - (id) copyWithZone: (NSZone*)zone
78 GGBLayer *clone = [[[self class] alloc] init];
79 clone.bounds = self.bounds;
80 clone.position = self.position;
81 clone.zPosition = self.zPosition;
82 clone.anchorPoint = self.anchorPoint;
83 clone.transform = self.transform;
84 clone.hidden = self.hidden;
85 clone.doubleSided = self.doubleSided;
86 clone.sublayerTransform = self.sublayerTransform;
87 clone.masksToBounds = self.masksToBounds;
88 clone.contents = self.contents; // doesn't copy contents (shallow-copy)
89 clone.contentsRect = self.contentsRect;
90 clone.contentsGravity = self.contentsGravity;
91 clone.minificationFilter = self.minificationFilter;
92 clone.magnificationFilter = self.magnificationFilter;
93 clone.opaque = self.opaque;
94 clone.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange;
95 clone.edgeAntialiasingMask = self.edgeAntialiasingMask;
96 clone.backgroundColor = self.backgroundColor;
97 clone.opacity = self.opacity;
98 clone.compositingFilter = self.compositingFilter;
99 clone.filters = self.filters;
100 clone.backgroundFilters = self.backgroundFilters;
101 clone.actions = self.actions;
102 clone.name = self.name;
103 clone.style = self.style;
105 clone.cornerRadius = self.cornerRadius;
106 clone.borderWidth = self.borderWidth;
107 clone.borderColor = self.borderColor;
109 for( GGBLayer *sublayer in self.sublayers ) {
110 sublayer = [sublayer copyWithZone: zone];
111 [clone addSublayer: sublayer];
117 - (CGFloat) cornerRadius {return _cornerRadius;}
118 - (CGFloat) borderWidth {return _borderWidth;}
119 - (CGColorRef) backgroundColor {return _realBGColor;}
120 - (CGColorRef) borderColor {return _borderColor;}
122 - (void) setCornerRadius: (CGFloat)r
124 if( r != _cornerRadius ) {
126 [self setNeedsDisplay];
131 - (void) setBorderWidth: (CGFloat)w
133 if( w != _borderWidth ) {
135 self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
136 [self setNeedsDisplay];
141 - (void) setBackgroundColor: (CGColorRef)color
143 if( color != _realBGColor ) {
144 CGColorRelease(_realBGColor);
145 _realBGColor = CGColorRetain(color);
146 [self setNeedsDisplay];
151 - (void) setBorderColor: (CGColorRef)color
153 if( color != _borderColor ) {
154 CGColorRelease(_borderColor);
155 _borderColor = CGColorRetain(color);
156 self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
157 [self setNeedsDisplay];
162 - (void)drawInContext:(CGContextRef)ctx
164 [super drawInContext: ctx];
166 CGContextSaveGState(ctx);
169 CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth);
170 CGContextSetFillColorWithColor(ctx, _realBGColor);
171 if( _cornerRadius <= 0.0 ) {
172 CGContextFillRect(ctx,interior);
174 CGContextBeginPath(ctx);
175 AddRoundRect(ctx,interior,_cornerRadius);
176 CGContextFillPath(ctx);
180 if( _borderWidth > 0.0 && _borderColor!=NULL ) {
181 CGRect border = CGRectInset(self.bounds, _borderWidth/2.0, _borderWidth/2.0);
182 CGContextSetStrokeColorWithColor(ctx, _borderColor);
183 CGContextSetLineWidth(ctx, _borderWidth);
185 if( _cornerRadius <= 0.0 ) {
186 CGContextStrokeRect(ctx,border);
188 CGContextBeginPath(ctx);
189 AddRoundRect(ctx,border,_cornerRadius);
190 CGContextStrokePath(ctx);
194 CGContextRestoreGState(ctx);
201 #pragma mark MAC OS VERSION:
204 - (id) copyWithZone: (NSZone*)zone
206 // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to
207 // a temporary data block, then unarchiving a new layer from that block.
209 // One complication is that, due to a bug in Core Animation, CALayer can't archive
210 // a pattern-based CGColor. So as a workaround, clear the background before archiving,
211 // then restore it afterwards.
213 // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 )
214 // As a workaround, clear the contents before archiving, then restore.
216 CGColorRef bg = CGColorRetain(self.backgroundColor);
217 self.backgroundColor = NULL;
218 id contents = [self.contents retain];
221 NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
223 self.backgroundColor = bg;
224 self.contents = contents;
226 GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
227 clone.backgroundColor = bg;
228 clone.contents = contents;
232 return [clone retain];
244 #pragma mark UTILITIES:
247 void BeginDisableAnimations(void)
249 [CATransaction begin];
250 [CATransaction setValue:(id)kCFBooleanTrue
251 forKey:kCATransactionDisableActions];
254 void EndDisableAnimations(void)
256 [CATransaction commit];
260 void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
262 // Disable actions, else the layer will move to the wrong place and then back!
263 [CATransaction flush];
264 BeginDisableAnimations();
266 CGPoint pos = layer.position;
267 if( layer.superlayer )
268 pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer];
270 [layer removeFromSuperlayer];
271 layer.position = pos;
273 [newSuperlayer insertSublayer: layer atIndex: index];
275 [newSuperlayer addSublayer: layer];
278 EndDisableAnimations();
282 void RemoveImmediately( CALayer *layer )
284 [CATransaction flush];
285 BeginDisableAnimations();
286 [layer removeFromSuperlayer];
287 EndDisableAnimations();
291 CGColorRef GetEffectiveBackground( CALayer *layer )
293 for( ; layer; layer=layer.superlayer ) {
294 CGColorRef bg = layer.backgroundColor;