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