jens@1: // jens@1: // GGBLayer.m jens@1: // GGB-iPhone jens@1: // jens@1: // Created by Jens Alfke on 3/7/08. jens@1: // Copyright 2008 __MyCompanyName__. All rights reserved. jens@1: // jens@1: jens@1: #import "GGBLayer.h" jens@1: #import "QuartzUtils.h" jens@1: jens@1: jens@1: @implementation GGBLayer jens@1: jens@1: jens@1: - (NSString*) description jens@1: { jens@1: return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y]; jens@1: } jens@1: jens@1: jens@4: - (void) redisplayAll jens@4: { jens@4: [self setNeedsDisplay]; jens@4: for( CALayer *layer in self.sublayers ) jens@4: if( [layer isKindOfClass: [GGBLayer class]] ) jens@4: ((GGBLayer*)layer).redisplayAll; jens@4: else jens@4: [layer setNeedsDisplay]; jens@4: } jens@4: jens@4: jens@7: /* jens@7: - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key jens@7: { jens@7: NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key); jens@7: [super addAnimation: anim forKey: key]; jens@7: } jens@7: */ jens@7: jens@7: jens@7: - (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration jens@7: { jens@7: //WARNING: This code works, but is a mess. I hope to find a better way to do this. --Jens 3/16/08 jens@7: CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath: keyPath]; jens@7: anim.duration= duration; jens@7: anim.fromValue = from; jens@7: anim.toValue = to; jens@7: anim.isRemovedOnCompletion = YES; jens@7: anim.delegate = self; jens@7: [self addAnimation:anim forKey: @"animateAndBlock:"]; jens@7: _curAnimation = (id)[self animationForKey: @"animateAndBlock:"]; jens@7: [self setValue: to forKeyPath: keyPath]; // animation doesn't update the property value jens@7: jens@7: // Now wait for it to finish: jens@7: while( _curAnimation ) { jens@7: [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode jens@7: beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]]; jens@7: } jens@7: } jens@7: jens@7: - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag jens@7: { jens@7: if( anim==_curAnimation ) { jens@7: _curAnimation = nil; jens@7: } jens@7: } jens@7: jens@7: jens@8: #if TARGET_OS_IPHONE jens@1: jens@1: #pragma mark - jens@1: #pragma mark IPHONE VERSION: jens@1: jens@1: jens@1: - (id) copyWithZone: (NSZone*)zone jens@1: { jens@1: GGBLayer *clone = [[[self class] alloc] init]; jens@1: clone.bounds = self.bounds; jens@1: clone.position = self.position; jens@1: clone.zPosition = self.zPosition; jens@1: clone.anchorPoint = self.anchorPoint; jens@1: clone.transform = self.transform; jens@1: clone.hidden = self.hidden; jens@1: clone.doubleSided = self.doubleSided; jens@1: clone.sublayerTransform = self.sublayerTransform; jens@1: clone.masksToBounds = self.masksToBounds; jens@1: clone.contents = self.contents; // doesn't copy contents (shallow-copy) jens@1: clone.contentsRect = self.contentsRect; jens@1: clone.contentsGravity = self.contentsGravity; jens@1: clone.minificationFilter = self.minificationFilter; jens@1: clone.magnificationFilter = self.magnificationFilter; jens@1: clone.opaque = self.opaque; jens@1: clone.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange; jens@1: clone.edgeAntialiasingMask = self.edgeAntialiasingMask; jens@1: clone.backgroundColor = self.backgroundColor; jens@1: clone.opacity = self.opacity; jens@1: clone.compositingFilter = self.compositingFilter; jens@1: clone.filters = self.filters; jens@1: clone.backgroundFilters = self.backgroundFilters; jens@1: clone.actions = self.actions; jens@1: clone.name = self.name; jens@1: clone.style = self.style; jens@1: jens@1: clone.cornerRadius = self.cornerRadius; jens@1: clone.borderWidth = self.borderWidth; jens@1: clone.borderColor = self.borderColor; jens@1: jens@1: for( GGBLayer *sublayer in self.sublayers ) { jens@1: sublayer = [sublayer copyWithZone: zone]; jens@1: [clone addSublayer: sublayer]; jens@1: } jens@1: return clone; jens@1: } jens@1: jens@1: jens@1: - (CGFloat) cornerRadius {return _cornerRadius;} jens@1: - (CGFloat) borderWidth {return _borderWidth;} jens@1: - (CGColorRef) borderColor {return _borderColor;} jens@1: jens@1: - (void) setCornerRadius: (CGFloat)r jens@1: { jens@1: if( r != _cornerRadius ) { jens@1: _cornerRadius = r; jens@1: [self setNeedsDisplay]; jens@1: } jens@1: } jens@1: jens@1: jens@1: - (void) setBorderWidth: (CGFloat)w jens@1: { jens@1: if( w != _borderWidth ) { jens@1: _borderWidth = w; jens@1: self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL); jens@1: [self setNeedsDisplay]; jens@1: } jens@1: } jens@1: jens@1: jens@1: - (void) setBackgroundColor: (CGColorRef)color jens@1: { jens@1: if( color != _realBGColor ) { jens@1: CGColorRelease(_realBGColor); jens@1: _realBGColor = CGColorRetain(color); jens@1: [self setNeedsDisplay]; jens@1: } jens@1: } jens@1: jens@1: jens@1: - (void) setBorderColor: (CGColorRef)color jens@1: { jens@1: if( color != _borderColor ) { jens@1: CGColorRelease(_borderColor); jens@1: _borderColor = CGColorRetain(color); jens@1: self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL); jens@1: [self setNeedsDisplay]; jens@1: } jens@1: } jens@1: jens@1: jens@1: - (void)drawInContext:(CGContextRef)ctx jens@1: { jens@4: [super drawInContext: ctx]; jens@4: jens@1: CGContextSaveGState(ctx); jens@1: jens@1: if( _realBGColor ) { jens@1: CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth); jens@1: CGContextSetFillColorWithColor(ctx, _realBGColor); jens@1: if( _cornerRadius <= 0.0 ) { jens@1: CGContextFillRect(ctx,interior); jens@1: } else { jens@1: CGContextBeginPath(ctx); jens@1: AddRoundRect(ctx,interior,_cornerRadius); jens@1: CGContextFillPath(ctx); jens@1: } jens@1: } jens@1: jens@1: if( _borderWidth > 0.0 && _borderColor!=NULL ) { jens@1: CGRect border = CGRectInset(self.bounds, _borderWidth/2.0, _borderWidth/2.0); jens@1: CGContextSetStrokeColorWithColor(ctx, _borderColor); jens@1: CGContextSetLineWidth(ctx, _borderWidth); jens@1: jens@1: if( _cornerRadius <= 0.0 ) { jens@1: CGContextStrokeRect(ctx,border); jens@1: } else { jens@1: CGContextBeginPath(ctx); jens@1: AddRoundRect(ctx,border,_cornerRadius); jens@1: CGContextStrokePath(ctx); jens@1: } jens@1: } jens@1: jens@1: CGContextRestoreGState(ctx); jens@1: } jens@1: jens@1: jens@1: #else jens@1: jens@1: #pragma mark - jens@1: #pragma mark MAC OS VERSION: jens@1: jens@1: jens@1: - (id) copyWithZone: (NSZone*)zone jens@1: { jens@1: // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to jens@1: // a temporary data block, then unarchiving a new layer from that block. jens@1: jens@1: // One complication is that, due to a bug in Core Animation, CALayer can't archive jens@1: // a pattern-based CGColor. So as a workaround, clear the background before archiving, jens@1: // then restore it afterwards. jens@1: jens@1: // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 ) jens@1: // As a workaround, clear the contents before archiving, then restore. jens@1: jens@1: CGColorRef bg = CGColorRetain(self.backgroundColor); jens@1: self.backgroundColor = NULL; jens@1: id contents = [self.contents retain]; jens@1: self.contents = nil; jens@1: jens@1: NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self]; jens@1: jens@1: self.backgroundColor = bg; jens@1: self.contents = contents; jens@1: jens@1: GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data]; jens@1: clone.backgroundColor = bg; jens@1: clone.contents = contents; jens@1: CGColorRelease(bg); jens@1: [contents release]; jens@1: jens@1: return [clone retain]; jens@1: } jens@1: jens@1: jens@1: #endif jens@1: jens@1: jens@1: @end