Created
November 28, 2010 00:25
-
-
Save PsychoH13/718428 to your computer and use it in GitHub Desktop.
Layout manager using direct layer references
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* PSYLayerConstraintLayoutManager.h | |
* LayoutManager | |
* | |
* Created by Remy Demarest on 27/02/2010. | |
* Copyright (c) 2010, Remy Demarest | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* * Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* * Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* * Neither the name of the OpenEmu Team nor the | |
* names of its contributors may be used to endorse or promote products | |
* derived from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY Remy Demarest ''AS IS'' AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#import <Cocoa/Cocoa.h> | |
#import <QuartzCore/QuartzCore.h> | |
extern CALayer *const kPSYSuperLayer; | |
@interface PSYLayerConstraintLayoutManager : NSObject | |
+ (id)layoutManager; | |
@end | |
@interface PSYLayerConstraint : NSObject | |
{ | |
@private | |
__weak CALayer *_srcLayer; | |
CAConstraintAttribute _srcAttr :16; | |
CAConstraintAttribute _attr :16; | |
CGFloat _scale, _offset; | |
} | |
/* Create a new constraint object with the specified parameters. In the | |
* general case the new constraint will have the form: | |
* | |
* layer.attr = m * srcLayer.srcAttr + c | |
* | |
* 'm' defaults to one when undefined; 'c' defaults to zero. */ | |
+ (id)constraintWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
scale:(CGFloat)m offset:(CGFloat)c; | |
+ (id)constraintWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
offset:(CGFloat)c; | |
+ (id)constraintWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr; | |
/* Designated initializer. */ | |
- (id)initWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
scale:(CGFloat)m offset:(CGFloat)c; | |
/* Accessors. */ | |
@property(readonly) CALayer *sourceLayer; | |
@property(readonly) CAConstraintAttribute attribute; | |
@property(readonly) CAConstraintAttribute sourceAttribute; | |
@property(readonly) CGFloat scale, offset; | |
@end | |
@interface CALayer (PSYLayerConstraintLayoutManager) | |
@property(copy) NSArray *layerConstraints; | |
- (void)addLayerConstraint:(PSYLayerConstraint *)aConstraint; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Created by Remy Demarest on 27/02/2010. | |
* Copyright (c) 2010, Remy Demarest | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* * Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* * Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* * Neither the name of the OpenEmu Team nor the | |
* names of its contributors may be used to endorse or promote products | |
* derived from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY Remy Demarest ''AS IS'' AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#import "PSYLayerConstraintLayoutManager.h" | |
CALayer *const kPSYSuperLayer = nil; | |
@interface CALayer (Private) | |
@property CGSize sizeRequisition; | |
@end | |
@implementation PSYLayerConstraintLayoutManager | |
+ (void)initialize | |
{ | |
if(self == [PSYLayerConstraintLayoutManager class]) | |
{ | |
*((id *)&kPSYSuperLayer) = [CALayer class]; | |
} | |
} | |
+ (id)layoutManager | |
{ | |
static PSYLayerConstraintLayoutManager *layoutManager = nil; | |
if(layoutManager != nil) return [layoutManager retain]; | |
@synchronized(self) | |
{ | |
if(layoutManager == nil) | |
layoutManager = [[PSYLayerConstraintLayoutManager alloc] init]; | |
} | |
return layoutManager; | |
} | |
typedef struct __attrDefs | |
{ | |
NSUInteger count; | |
CAConstraintAttribute types[2]; | |
CGFloat values[2]; | |
} __attrDefs; | |
- (void)layoutSublayersOfLayer:(CALayer *)layer | |
{ | |
CGRect bounds = [layer bounds]; | |
NSArray *sublayers = [layer sublayers]; | |
NSMutableSet *laidOutSublayers = [NSMutableSet setWithCapacity:[sublayers count]]; | |
__block void (^layoutSublayer)(CALayer *sublayer) = | |
^ void (CALayer *sublayer) | |
{ | |
if([laidOutSublayers containsObject:sublayer]) return; | |
[laidOutSublayers addObject:sublayer]; | |
NSArray *constraints = [sublayer layerConstraints]; | |
if([constraints count] == 0) return; | |
// These structures cache constraint attributes for each axis | |
__attrDefs vertical; | |
__attrDefs horizontal; | |
bzero(&vertical, sizeof(vertical)); | |
bzero(&horizontal, sizeof(horizontal)); | |
// Constraint read, it only reads 2 constraints per axix | |
for(PSYLayerConstraint *constraint in constraints) | |
{ | |
if(vertical.count >= 2 && horizontal.count >= 2) break; | |
CALayer *srcLayer = [constraint sourceLayer]; | |
if(srcLayer != kPSYSuperLayer) | |
{ | |
if(srcLayer == sublayer) continue; | |
if([srcLayer superlayer] == layer) layoutSublayer(srcLayer); | |
} | |
CAConstraintAttribute type = [constraint attribute]; | |
__attrDefs *current = NULL; | |
if(type < kCAConstraintMinY) current = &horizontal; | |
else current = &vertical; | |
// If the axis is full, just continue | |
if(current->count >= 2) continue; | |
// If the value is already defined for this axis attribute, just continue | |
if(current->count == 1 && current->types[0] == type) continue; | |
current->types[current->count] = type; | |
CGRect srcFrame = CGRectZero; | |
if(srcLayer == kPSYSuperLayer) | |
{ | |
srcLayer = layer; | |
srcFrame = bounds; | |
} | |
else srcFrame = [layer convertRect:[srcLayer bounds] fromLayer:srcLayer]; | |
CGFloat srcAttr = 0.0; | |
switch([constraint sourceAttribute]) | |
{ | |
case kCAConstraintMinX : srcAttr = CGRectGetMinX(srcFrame); break; | |
case kCAConstraintMidX : srcAttr = CGRectGetMidX(srcFrame); break; | |
case kCAConstraintMaxX : srcAttr = CGRectGetMaxX(srcFrame); break; | |
case kCAConstraintWidth : srcAttr = CGRectGetWidth(srcFrame); break; | |
case kCAConstraintMinY : srcAttr = CGRectGetMinY(srcFrame); break; | |
case kCAConstraintMidY : srcAttr = CGRectGetMidY(srcFrame); break; | |
case kCAConstraintMaxY : srcAttr = CGRectGetMaxY(srcFrame); break; | |
case kCAConstraintHeight : srcAttr = CGRectGetHeight(srcFrame); break; | |
} | |
current->values[current->count] = srcAttr * [constraint scale] + [constraint offset]; | |
current->count++; | |
} | |
CGRect frame = [sublayer frame]; | |
#define SWAP(a, b) do { \ | |
__typeof(a) temp = a; \ | |
a = b; \ | |
b = temp; \ | |
} while(0) | |
#define SET_AXIS(defs, axis, length, lowest) do { \ | |
if(defs.count == 2 && defs.types[0] > defs.types[1]) \ | |
{ \ | |
SWAP(defs.types[0], defs.types[1]); \ | |
SWAP(defs.values[0], defs.values[1]); \ | |
} \ | |
const CAConstraintAttribute attr0 = defs.types[0]; \ | |
const CAConstraintAttribute attr1 = defs.types[1]; \ | |
const CGFloat value0 = defs.values[0]; \ | |
const CGFloat value1 = defs.values[1]; \ | |
switch(defs.count) \ | |
{ \ | |
case 1 : \ | |
{ \ | |
CGFloat value = defs.values[0]; \ | |
switch(attr0) \ | |
{ \ | |
case kCAConstraintMinX + lowest : frame.origin.axis = value0; break; \ | |
case kCAConstraintMidX + lowest : frame.origin.axis = value0 - frame.size.length / 2.0; break; \ | |
case kCAConstraintMaxX + lowest : frame.origin.axis = value0 - frame.size.length; break; \ | |
case kCAConstraintWidth + lowest : frame.size.length = value0; break; \ | |
} \ | |
} break; \ | |
case 2 : \ | |
{ \ | |
/* 6 cases to treat here */ \ | |
/* cases kCAConstraintMinX / kCAConstraintMinY and kCAConstraintWidth / kCAConstraintHeight are managed by these 2 if */ \ | |
if((kCAConstraintMinX + lowest) == attr0) frame.origin.axis = value0; \ | |
if((kCAConstraintWidth + lowest) == attr1) frame.size.length = value1; \ | |
\ | |
/* case kCAConstraintMinX / kCAConstraintMinY and kCAConstraintMidX / kCAConstraintMidY */ \ | |
if((kCAConstraintMinX + lowest) == attr0 && (kCAConstraintMidX + lowest) == attr1) \ | |
frame.size.length = (value1 - value0) * 2.0; \ | |
\ | |
/* case kCAConstraintMinX / kCAConstraintMinY and kCAConstraintMaxX / kCAConstraintMaxY */ \ | |
else if((kCAConstraintMinX + lowest) == attr0 && (kCAConstraintMaxX + lowest) == attr1) \ | |
frame.size.length = (value1 - value0); \ | |
\ | |
/* case kCAConstraintMidX / kCAConstraintMidY and kCAConstraintMaxX / kCAConstraintMaxY */ \ | |
else if((kCAConstraintMidX + lowest) == attr0 && (kCAConstraintMaxX + lowest) == attr1) \ | |
{ \ | |
CGFloat halfWidth = value1 - value0; \ | |
frame.origin.axis = value0 - halfWidth; \ | |
frame.size.length = halfWidth * 2.0; \ | |
} \ | |
\ | |
/* case kCAConstraintMidX / kCAConstraintMidY and kCAConstraintWidth / kCAConstraintHeight */ \ | |
else if((kCAConstraintMidX + lowest) == attr0 && (kCAConstraintWidth + lowest) == attr1) \ | |
frame.origin.axis = value0 - value1 / 2.0; \ | |
\ | |
/* case kCAConstraintMaxX / kCAConstraintMaxY and kCAConstraintWidth / kCAConstraintHeight */ \ | |
else if((kCAConstraintMaxX + lowest) == attr0 && (kCAConstraintWidth + lowest) == attr1) \ | |
frame.origin.axis = value0 - value1; \ | |
} break; \ | |
} \ | |
} while(0) | |
SET_AXIS(horizontal, x, width, kCAConstraintMinX); | |
SET_AXIS(vertical, y, height, kCAConstraintMinY); | |
[sublayer setFrame:frame]; | |
}; | |
for(CALayer *sub in sublayers) layoutSublayer(sub); | |
} | |
- (void)invalidateLayoutOfLayer:(CALayer *)layer | |
{ | |
[layer setSizeRequisition:CGSizeZero]; | |
} | |
- (CGSize)preferredSizeOfLayer:(CALayer *)layer | |
{ | |
CGSize ret = [layer sizeRequisition]; | |
if(!CGSizeEqualToSize(ret, CGSizeZero)) return ret; | |
return ret; | |
} | |
@end | |
@implementation PSYLayerConstraint | |
@synthesize sourceLayer = _srcLayer, attribute = _attr, sourceAttribute = _srcAttr, scale = _scale, offset = _offset; | |
/* Create a new constraint object with the specified parameters. In the | |
* general case the new constraint will have the form: | |
* | |
* layer.attr = m * srcLayer.srcAttr + c | |
* | |
* 'm' defaults to one when undefined; 'c' defaults to zero. */ | |
+ (id)constraintWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
scale:(CGFloat)m offset:(CGFloat)c | |
{ | |
return [[[self alloc] initWithAttribute:attr relativeToLayer:srcLayer attribute:srcAttr scale:m offset:c] autorelease]; | |
} | |
+ (id)constraintWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
offset:(CGFloat)c | |
{ | |
return [self constraintWithAttribute:attr relativeToLayer:srcLayer attribute:srcAttr scale:1.0 offset:c]; | |
} | |
+ (id)constraintWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
{ | |
return [self constraintWithAttribute:attr relativeToLayer:srcLayer attribute:srcAttr offset:0.0]; | |
} | |
/* Designated initializer. */ | |
- (id)initWithAttribute:(CAConstraintAttribute)attr | |
relativeToLayer:(CALayer *)srcLayer attribute:(CAConstraintAttribute)srcAttr | |
scale:(CGFloat)m offset:(CGFloat)c | |
{ | |
self = [super init]; | |
if(self != nil) | |
{ | |
_srcLayer = srcLayer; | |
_attr = attr; | |
_srcAttr = srcAttr; | |
_scale = m; | |
_offset = c; | |
} | |
return self; | |
} | |
@end | |
@implementation CALayer (PSYLayerConstraintLayoutManager) | |
- (NSArray *)layerConstraints { return [self valueForKey:@"PSY_layerConstraints"]; } | |
- (void)setLayerConstraints:(NSArray *)value | |
{ | |
[self setValue:[[value copy] autorelease] forKey:@"PSY_layerConstraints"]; | |
} | |
- (void)addLayerConstraint:(PSYLayerConstraint *)aConstraint | |
{ | |
NSArray *cs = [self layerConstraints]; | |
if(cs == nil) cs = [NSArray array]; | |
[self setLayerConstraints:[cs arrayByAddingObject:aConstraint]]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment