Source/GGBLayer.m
author Jens Alfke <jens@mooseyard.com>
Thu Jul 03 17:44:30 2008 -0700 (2008-07-03)
changeset 10 6c78cc6bd7a6
parent 9 a59acc683080
child 11 436cbdf56810
permissions -rw-r--r--
Lots of reworking. Completed support for game history, including Turn class. Changed Game API around quite a bit.
     1 //
     2 //  GGBLayer.m
     3 //  GGB-iPhone
     4 //
     5 //  Created by Jens Alfke on 3/7/08.
     6 //  Copyright 2008 __MyCompanyName__. All rights reserved.
     7 //
     8 
     9 #import "GGBLayer.h"
    10 #import "QuartzUtils.h"
    11 
    12 
    13 @implementation GGBLayer
    14 
    15 
    16 - (NSString*) description
    17 {
    18     return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
    19 }
    20 
    21 
    22 - (void) redisplayAll
    23 {
    24     [self setNeedsDisplay];
    25     for( CALayer *layer in self.sublayers )
    26         if( [layer isKindOfClass: [GGBLayer class]] )
    27             ((GGBLayer*)layer).redisplayAll;
    28         else
    29             [layer setNeedsDisplay];
    30 }
    31 
    32 
    33 /*
    34 - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key 
    35 {
    36     NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key);
    37     [super addAnimation: anim forKey: key];
    38 }
    39 */
    40 
    41 
    42 - (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration
    43 {
    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;
    48     anim.toValue = to;
    49     anim.isRemovedOnCompletion = YES;
    50     anim.delegate = self;
    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
    54 
    55     // Now wait for it to finish:
    56     while( _curAnimation ) {
    57         [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode
    58                                  beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
    59     }
    60 }
    61 
    62 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    63 {
    64     if( anim==_curAnimation ) {
    65         _curAnimation = nil;
    66     }
    67 }
    68 
    69 
    70 #if TARGET_OS_IPHONE
    71 
    72 #pragma mark -
    73 #pragma mark IPHONE VERSION:
    74 
    75 
    76 - (id) copyWithZone: (NSZone*)zone
    77 {
    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;
   104     
   105     clone.cornerRadius = self.cornerRadius;
   106     clone.borderWidth = self.borderWidth;
   107     clone.borderColor = self.borderColor;
   108     
   109     for( GGBLayer *sublayer in self.sublayers ) {
   110         sublayer = [sublayer copyWithZone: zone];
   111         [clone addSublayer: sublayer];
   112     }
   113     return clone;
   114 }
   115 
   116 
   117 - (CGFloat) cornerRadius    {return _cornerRadius;}
   118 - (CGFloat) borderWidth     {return _borderWidth;}
   119 - (CGColorRef) backgroundColor {return _realBGColor;}
   120 - (CGColorRef) borderColor  {return _borderColor;}
   121 
   122 - (void) setCornerRadius: (CGFloat)r
   123 {
   124     if( r != _cornerRadius ) {
   125         _cornerRadius = r;
   126         [self setNeedsDisplay];
   127     }
   128 }
   129 
   130 
   131 - (void) setBorderWidth: (CGFloat)w
   132 {
   133     if( w != _borderWidth ) {
   134         _borderWidth = w;
   135         self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
   136         [self setNeedsDisplay];
   137     }
   138 }
   139 
   140 
   141 - (void) setBackgroundColor: (CGColorRef)color
   142 {
   143     if( color != _realBGColor ) {
   144         CGColorRelease(_realBGColor);
   145         _realBGColor = CGColorRetain(color);
   146         [self setNeedsDisplay];
   147     }
   148 }
   149 
   150 
   151 - (void) setBorderColor: (CGColorRef)color
   152 {
   153     if( color != _borderColor ) {
   154         CGColorRelease(_borderColor);
   155         _borderColor = CGColorRetain(color);
   156         self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
   157         [self setNeedsDisplay];
   158     }
   159 }
   160 
   161 
   162 - (void)drawInContext:(CGContextRef)ctx
   163 {
   164     [super drawInContext: ctx];
   165     
   166     CGContextSaveGState(ctx);
   167 
   168     if( _realBGColor ) {
   169         CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth);
   170         CGContextSetFillColorWithColor(ctx, _realBGColor);
   171         if( _cornerRadius <= 0.0 ) {
   172             CGContextFillRect(ctx,interior);
   173         } else {
   174             CGContextBeginPath(ctx);
   175             AddRoundRect(ctx,interior,_cornerRadius);
   176             CGContextFillPath(ctx);
   177         }
   178     }
   179     
   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);
   184         
   185         if( _cornerRadius <= 0.0 ) {
   186             CGContextStrokeRect(ctx,border);
   187         } else {
   188             CGContextBeginPath(ctx);
   189             AddRoundRect(ctx,border,_cornerRadius);
   190             CGContextStrokePath(ctx);
   191         }
   192     }
   193     
   194     CGContextRestoreGState(ctx);
   195 }
   196 
   197 
   198 #else
   199 
   200 #pragma mark -
   201 #pragma mark MAC OS VERSION:
   202 
   203 
   204 - (id) copyWithZone: (NSZone*)zone
   205 {
   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.
   208     
   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.
   212     
   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.
   215     
   216     CGColorRef bg = CGColorRetain(self.backgroundColor);
   217     self.backgroundColor = NULL;
   218     id contents = [self.contents retain];
   219     self.contents = nil;
   220     
   221     NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
   222     
   223     self.backgroundColor = bg;
   224     self.contents = contents;
   225 
   226     GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
   227     clone.backgroundColor = bg;
   228     clone.contents = contents;
   229     CGColorRelease(bg);
   230     [contents release];
   231 
   232     return [clone retain];
   233 }
   234 
   235 
   236 #endif
   237 
   238 
   239 @end
   240 
   241 
   242 
   243 #pragma mark -
   244 #pragma mark UTILITIES:
   245 
   246 
   247 void BeginDisableAnimations(void)
   248 {
   249     [CATransaction begin];
   250     [CATransaction setValue:(id)kCFBooleanTrue
   251                      forKey:kCATransactionDisableActions];
   252 }
   253 
   254 void EndDisableAnimations(void)
   255 {
   256     [CATransaction commit];
   257 } 
   258 
   259 
   260 void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
   261 {
   262     // Disable actions, else the layer will move to the wrong place and then back!
   263     [CATransaction flush];
   264     BeginDisableAnimations();
   265     
   266     CGPoint pos = layer.position;
   267     if( layer.superlayer )
   268         pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer];
   269     [layer retain];
   270     [layer removeFromSuperlayer];
   271     layer.position = pos;
   272     if( index >= 0 )
   273         [newSuperlayer insertSublayer: layer atIndex: index];
   274     else
   275         [newSuperlayer addSublayer: layer];
   276     [layer release];
   277     
   278     EndDisableAnimations();
   279 }
   280 
   281 
   282 void RemoveImmediately( CALayer *layer )
   283 {
   284     [CATransaction flush];
   285     BeginDisableAnimations();
   286     [layer removeFromSuperlayer];
   287     EndDisableAnimations();
   288 }    
   289 
   290 
   291 CGColorRef GetEffectiveBackground( CALayer *layer )
   292 {
   293     for( ; layer; layer=layer.superlayer ) {
   294         CGColorRef bg = layer.backgroundColor;
   295         if( bg )
   296             return bg;
   297     }
   298     return nil;
   299 }