Source/GGBLayer.m
author Jens Alfke <jens@mooseyard.com>
Tue Jul 08 20:32:52 2008 -0700 (2008-07-08)
changeset 14 4585c74d809c
parent 11 436cbdf56810
child 15 73f8c889f053
permissions -rw-r--r--
Minor fixes.
     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 #import "GGBUtils.h"
    12 
    13 
    14 NSString* const GGBLayerStyleChangedNotification = @"GGBLayerStyleChanged";
    15 
    16 
    17 @implementation GGBLayer
    18 
    19 
    20 - (NSString*) description
    21 {
    22     return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
    23 }
    24 
    25 
    26 - (void) redisplayAll
    27 {
    28     [self setNeedsDisplay];
    29     for( CALayer *layer in self.sublayers )
    30         if( [layer isKindOfClass: [GGBLayer class]] )
    31             ((GGBLayer*)layer).redisplayAll;
    32         else
    33             [layer setNeedsDisplay];
    34 }
    35 
    36 
    37 /*
    38 - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key 
    39 {
    40     NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key);
    41     [super addAnimation: anim forKey: key];
    42 }
    43 */
    44 
    45 
    46 - (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration
    47 {
    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;
    52     anim.toValue = to;
    53     anim.isRemovedOnCompletion = YES;
    54     anim.delegate = self;
    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
    58 
    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]];
    64         }
    65     } else {
    66         _curAnimation = nil;
    67     }
    68 }
    69 
    70 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    71 {
    72     if( anim==_curAnimation ) {
    73         _curAnimation = nil;
    74     }
    75 }
    76 
    77 
    78 - (void) setStyle: (NSDictionary*)style
    79 {
    80     if( style != _styleDict ) {
    81         if( _styleDict )
    82             [[NSNotificationCenter defaultCenter] removeObserver: self 
    83                                                             name: GGBLayerStyleChangedNotification
    84                                                           object: _styleDict];
    85         if( style )
    86             [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_styleChanged)
    87                                                          name: GGBLayerStyleChangedNotification
    88                                                        object: style];
    89         setObj(&_styleDict,style);
    90     }
    91     [super setStyle: style];
    92 }
    93 
    94 - (void) _styleChanged
    95 {
    96     // Reapply the style, so any changes in the dict will take effect.
    97     [super setStyle: _styleDict];
    98 }
    99 
   100 - (void) dealloc
   101 {
   102     if( _styleDict )
   103         [[NSNotificationCenter defaultCenter] removeObserver: self
   104                                                         name: GGBLayerStyleChangedNotification
   105                                                       object: _styleDict];
   106     [super dealloc];
   107 }
   108 
   109 
   110 - (void) setValue: (id)value ofStyleProperty: (NSString*)prop
   111 {
   112     if( _styleDict ) {
   113         id oldValue = [_styleDict objectForKey: prop];
   114         if( oldValue != value ) {
   115             if( value )
   116                 [_styleDict setObject: value forKey: prop];
   117             else
   118                 [_styleDict removeObjectForKey: prop];
   119             [[NSNotificationCenter defaultCenter] postNotificationName: GGBLayerStyleChangedNotification
   120                                                                 object: _styleDict];
   121         }
   122     } else
   123         [self setValue: value forKey: prop];
   124 }
   125 
   126 
   127 
   128 #if TARGET_OS_IPHONE
   129 
   130 #pragma mark -
   131 #pragma mark IPHONE VERSION:
   132 
   133 
   134 - (id) copyWithZone: (NSZone*)zone
   135 {
   136     GGBLayer *clone = [[[self class] alloc] init];
   137     clone.bounds = self.bounds;
   138     clone.position = self.position;
   139     clone.zPosition = self.zPosition;
   140     clone.anchorPoint = self.anchorPoint;
   141     clone.transform = self.transform;
   142     clone.hidden = self.hidden;
   143     clone.doubleSided = self.doubleSided;
   144     clone.sublayerTransform = self.sublayerTransform;
   145     clone.masksToBounds = self.masksToBounds;
   146     clone.contents = self.contents;                 // doesn't copy contents (shallow-copy)
   147     clone.contentsRect = self.contentsRect;
   148     clone.contentsGravity = self.contentsGravity;
   149     clone.minificationFilter = self.minificationFilter;
   150     clone.magnificationFilter = self.magnificationFilter;
   151     clone.opaque = self.opaque;
   152     clone.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange;
   153     clone.edgeAntialiasingMask = self.edgeAntialiasingMask;
   154     clone.backgroundColor = self.backgroundColor;
   155     clone.opacity = self.opacity;
   156     clone.compositingFilter = self.compositingFilter;
   157     clone.filters = self.filters;
   158     clone.backgroundFilters = self.backgroundFilters;
   159     clone.actions = self.actions;
   160     clone.name = self.name;
   161     clone.style = self.style;
   162     
   163     clone.cornerRadius = self.cornerRadius;
   164     clone.borderWidth = self.borderWidth;
   165     clone.borderColor = self.borderColor;
   166     
   167     for( GGBLayer *sublayer in self.sublayers ) {
   168         sublayer = [sublayer copyWithZone: zone];
   169         [clone addSublayer: sublayer];
   170     }
   171     return clone;
   172 }
   173 
   174 
   175 - (CGFloat) cornerRadius    {return _cornerRadius;}
   176 - (CGFloat) borderWidth     {return _borderWidth;}
   177 - (CGColorRef) backgroundColor {return _realBGColor;}
   178 - (CGColorRef) borderColor  {return _borderColor;}
   179 
   180 - (void) setCornerRadius: (CGFloat)r
   181 {
   182     if( r != _cornerRadius ) {
   183         _cornerRadius = r;
   184         [self setNeedsDisplay];
   185     }
   186 }
   187 
   188 
   189 - (void) setBorderWidth: (CGFloat)w
   190 {
   191     if( w != _borderWidth ) {
   192         _borderWidth = w;
   193         self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
   194         [self setNeedsDisplay];
   195     }
   196 }
   197 
   198 
   199 - (void) setBackgroundColor: (CGColorRef)color
   200 {
   201     if( color != _realBGColor ) {
   202         CGColorRelease(_realBGColor);
   203         _realBGColor = CGColorRetain(color);
   204         [self setNeedsDisplay];
   205     }
   206 }
   207 
   208 
   209 - (void) setBorderColor: (CGColorRef)color
   210 {
   211     if( color != _borderColor ) {
   212         CGColorRelease(_borderColor);
   213         _borderColor = CGColorRetain(color);
   214         self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
   215         [self setNeedsDisplay];
   216     }
   217 }
   218 
   219 
   220 - (void)drawInContext:(CGContextRef)ctx
   221 {
   222     [super drawInContext: ctx];
   223     
   224     CGContextSaveGState(ctx);
   225 
   226     if( _realBGColor ) {
   227         CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth);
   228         CGContextSetFillColorWithColor(ctx, _realBGColor);
   229         if( _cornerRadius <= 0.0 ) {
   230             CGContextFillRect(ctx,interior);
   231         } else {
   232             CGContextBeginPath(ctx);
   233             AddRoundRect(ctx,interior,_cornerRadius);
   234             CGContextFillPath(ctx);
   235         }
   236     }
   237     
   238     if( _borderWidth > 0.0 && _borderColor!=NULL ) {
   239         CGRect border = CGRectInset(self.bounds, _borderWidth/2.0, _borderWidth/2.0);
   240         CGContextSetStrokeColorWithColor(ctx, _borderColor);
   241         CGContextSetLineWidth(ctx, _borderWidth);
   242         
   243         if( _cornerRadius <= 0.0 ) {
   244             CGContextStrokeRect(ctx,border);
   245         } else {
   246             CGContextBeginPath(ctx);
   247             AddRoundRect(ctx,border,_cornerRadius);
   248             CGContextStrokePath(ctx);
   249         }
   250     }
   251     
   252     CGContextRestoreGState(ctx);
   253 }
   254 
   255 
   256 #else
   257 
   258 #pragma mark -
   259 #pragma mark MAC OS VERSION:
   260 
   261 
   262 - (id) copyWithZone: (NSZone*)zone
   263 {
   264     // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to
   265     // a temporary data block, then unarchiving a new layer from that block.
   266     
   267     // One complication is that, due to a bug in Core Animation, CALayer can't archive
   268     // a pattern-based CGColor. So as a workaround, clear the background before archiving,
   269     // then restore it afterwards.
   270     
   271     // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 )
   272     // As a workaround, clear the contents before archiving, then restore.
   273     
   274     CGColorRef bg = CGColorRetain(self.backgroundColor);
   275     self.backgroundColor = NULL;
   276     id contents = [self.contents retain];
   277     self.contents = nil;
   278     
   279     NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
   280     
   281     self.backgroundColor = bg;
   282     self.contents = contents;
   283 
   284     GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
   285     clone.backgroundColor = bg;
   286     clone.contents = contents;
   287     CGColorRelease(bg);
   288     [contents release];
   289 
   290     return [clone retain];
   291 }
   292 
   293 
   294 #endif
   295 
   296 
   297 @end
   298 
   299 
   300 
   301 #pragma mark -
   302 #pragma mark UTILITIES:
   303 
   304 
   305 void BeginDisableAnimations(void)
   306 {
   307     [CATransaction begin];
   308     [CATransaction setValue:(id)kCFBooleanTrue
   309                      forKey:kCATransactionDisableActions];
   310 }
   311 
   312 void EndDisableAnimations(void)
   313 {
   314     [CATransaction commit];
   315 } 
   316 
   317 
   318 void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
   319 {
   320     // Disable actions, else the layer will move to the wrong place and then back!
   321     [CATransaction flush];
   322     BeginDisableAnimations();
   323     
   324     CGPoint pos = layer.position;
   325     if( layer.superlayer )
   326         pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer];
   327     [layer retain];
   328     [layer removeFromSuperlayer];
   329     layer.position = pos;
   330     if( index >= 0 )
   331         [newSuperlayer insertSublayer: layer atIndex: index];
   332     else
   333         [newSuperlayer addSublayer: layer];
   334     [layer release];
   335     
   336     EndDisableAnimations();
   337 }
   338 
   339 
   340 void RemoveImmediately( CALayer *layer )
   341 {
   342     [CATransaction flush];
   343     BeginDisableAnimations();
   344     [layer removeFromSuperlayer];
   345     EndDisableAnimations();
   346 }    
   347 
   348 
   349 CGColorRef GetEffectiveBackground( CALayer *layer )
   350 {
   351     for( ; layer; layer=layer.superlayer ) {
   352         CGColorRef bg = layer.backgroundColor;
   353         if( bg )
   354             return bg;
   355     }
   356     return nil;
   357 }