mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-29 20:17:38 +00:00
Implement constraintsWithVisualFormat method on NSLayoutConstraint
This commit is contained in:
parent
30d0dc66ee
commit
1aff56cb9b
5 changed files with 569 additions and 5 deletions
|
@ -157,7 +157,12 @@ APPKIT_EXPORT_CLASS
|
|||
|
||||
- (NSLayoutAnchor *) secondAnchor;
|
||||
|
||||
- (NSLayoutPriority) priority;
|
||||
#if GS_HAS_DECLARED_PROPERTIES
|
||||
@property NSLayoutPriority priority;
|
||||
#else
|
||||
- (NSLayoutPriority) priority;
|
||||
- (void) setPriority: (NSLayoutPriority)priority;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -349,7 +349,8 @@ GSXibLoader.m \
|
|||
GSXibLoading.m \
|
||||
GSXibKeyedUnarchiver.m \
|
||||
GSXib5KeyedUnarchiver.m \
|
||||
GSHelpAttachment.m
|
||||
GSHelpAttachment.m \
|
||||
GSAutoLayoutVFLParser.m
|
||||
|
||||
# Turn off NSMenuItem warning that NSMenuItem conforms to <NSObject>,
|
||||
# but does not implement <NSObject>'s methods itself (it inherits
|
||||
|
@ -658,7 +659,8 @@ GSWindowDecorationView.h \
|
|||
GSXibElement.h \
|
||||
GSXibLoading.h \
|
||||
GSXibKeyedUnarchiver.h \
|
||||
GSHelpAttachment.h
|
||||
GSHelpAttachment.h \
|
||||
GSAutoLayoutVFLParser.h
|
||||
|
||||
libgnustep-gui_HEADER_FILES = ${GUI_HEADERS}
|
||||
|
||||
|
|
22
Source/GSAutoLayoutVFLParser.h
Normal file
22
Source/GSAutoLayoutVFLParser.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface GSAutoLayoutVFLParser : NSObject
|
||||
{
|
||||
NSDictionary *_views;
|
||||
NSLayoutFormatOptions _options;
|
||||
NSDictionary *_metrics;
|
||||
NSScanner *_scanner;
|
||||
NSMutableArray *_constraints;
|
||||
NSMutableArray *_layoutFormatConstraints;
|
||||
NSView *_view;
|
||||
BOOL _createLeadingConstraintToSuperview;
|
||||
BOOL _isVerticalOrientation;
|
||||
}
|
||||
|
||||
|
||||
-(instancetype)initWithFormat: (NSString*)format options: (NSLayoutFormatOptions)options metrics: (NSDictionary*)metrics views: (NSDictionary*)views;
|
||||
|
||||
-(NSArray*)parse;
|
||||
|
||||
@end
|
||||
|
526
Source/GSAutoLayoutVFLParser.m
Normal file
526
Source/GSAutoLayoutVFLParser.m
Normal file
|
@ -0,0 +1,526 @@
|
|||
#import "GSAutoLayoutVFLParser.h"
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
struct GSObjectOfPredicate {
|
||||
NSNumber *priority;
|
||||
NSView *view;
|
||||
NSLayoutRelation relation;
|
||||
CGFloat constant;
|
||||
};
|
||||
typedef struct GSObjectOfPredicate GSObjectOfPredicate;
|
||||
|
||||
NSInteger const GS_DEFAULT_VIEW_SPACING = 8;
|
||||
NSInteger const GS_DEFAULT_SUPERVIEW_SPACING = 20;
|
||||
|
||||
@implementation GSAutoLayoutVFLParser
|
||||
|
||||
-(instancetype)initWithFormat: (NSString*)format options: (NSLayoutFormatOptions)options metrics: (NSDictionary*)metrics views: (NSDictionary*)views
|
||||
{
|
||||
if (self = [super init]) {
|
||||
if ([format length] == 0) {
|
||||
[self failParseWithMessage:@"Cannot parse an empty string"];
|
||||
}
|
||||
|
||||
_views = views;
|
||||
_metrics = metrics;
|
||||
_options = options;
|
||||
|
||||
_scanner = [NSScanner scannerWithString:format];
|
||||
_constraints = [NSMutableArray array];
|
||||
_layoutFormatConstraints = [NSMutableArray array];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(NSArray*)parse
|
||||
{
|
||||
[self parseOrientation];
|
||||
NSNumber *spacingConstant = [self parseLeadingSuperViewConnection];
|
||||
NSView *previousView = nil;
|
||||
|
||||
while (![_scanner isAtEnd]) {
|
||||
NSArray *viewConstraints = [self parseView];
|
||||
if (_createLeadingConstraintToSuperview) {
|
||||
[self addLeadingSuperviewConstraint: spacingConstant];
|
||||
_createLeadingConstraintToSuperview = NO;
|
||||
}
|
||||
|
||||
if (previousView != nil) {
|
||||
[self addViewSpacingConstraint:spacingConstant previousView:previousView];
|
||||
[self addFormattingConstraints: previousView];
|
||||
}
|
||||
[_constraints addObjectsFromArray:viewConstraints];
|
||||
|
||||
spacingConstant = [self parseConnection];
|
||||
if ([_scanner scanString:@"|" intoString:nil]) {
|
||||
[self addTrailingToSuperviewConstraint: spacingConstant];
|
||||
}
|
||||
previousView = _view;
|
||||
}
|
||||
|
||||
[_constraints addObjectsFromArray:_layoutFormatConstraints];
|
||||
|
||||
return _constraints;
|
||||
}
|
||||
|
||||
-(void)addFormattingConstraints: (NSView*)lastView
|
||||
{
|
||||
BOOL hasFormatOptions = (_options & NSLayoutFormatAlignmentMask) > 0;
|
||||
if (!hasFormatOptions) {
|
||||
return;
|
||||
}
|
||||
[self assertHasValidFormatLayoutOptions];
|
||||
|
||||
NSArray *attributes = [self layoutAttributesForLayoutFormatOptions:_options];
|
||||
for (NSNumber *layoutAttribute in attributes) {
|
||||
NSLayoutConstraint *formatConstraint = [NSLayoutConstraint constraintWithItem:lastView attribute:[layoutAttribute integerValue] relatedBy:NSLayoutRelationEqual toItem:_view attribute:[layoutAttribute integerValue] multiplier:1.0 constant:0];
|
||||
[_layoutFormatConstraints addObject:formatConstraint];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)assertHasValidFormatLayoutOptions
|
||||
{
|
||||
if (_isVerticalOrientation && [self isVerticalEdgeFormatLayoutOption: _options]) {
|
||||
[self failParseWithMessage:@"A vertical alignment format option cannot be used with a vertical layout"];
|
||||
} else if (!_isVerticalOrientation && ![self isVerticalEdgeFormatLayoutOption:_options]) {
|
||||
[self failParseWithMessage:@"A horizontal alignment format option cannot be used with a horizontal layout"];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)parseOrientation
|
||||
{
|
||||
if ([_scanner scanString:@"V:" intoString:nil]) {
|
||||
_isVerticalOrientation = true;
|
||||
} else {
|
||||
[_scanner scanString:@"H:" intoString:nil];
|
||||
}
|
||||
}
|
||||
|
||||
-(NSArray*)parseView
|
||||
{
|
||||
[self parseViewOpen];
|
||||
|
||||
_view = [self parseViewName];
|
||||
NSArray *viewConstraints = [self parsePredicateList];
|
||||
[self parseViewClose];
|
||||
|
||||
return viewConstraints;
|
||||
}
|
||||
|
||||
-(NSView*)parseViewName
|
||||
{
|
||||
NSString *viewName = nil;
|
||||
NSCharacterSet *viewTerminators = [NSCharacterSet characterSetWithCharactersInString:@"]("];
|
||||
[_scanner scanUpToCharactersFromSet:viewTerminators intoString:&viewName];
|
||||
|
||||
if (viewName == nil) {
|
||||
[self failParseWithMessage:@"Failed to parse view name"];
|
||||
}
|
||||
|
||||
if (![self isValidIdentifer:viewName]) {
|
||||
[self failParseWithMessage:@"Invalid view name. A view name must be a valid C identifier and may only contain letters, numbers and underscores"];
|
||||
}
|
||||
|
||||
return [self resolveViewWithIdentifier:viewName];
|
||||
}
|
||||
|
||||
-(BOOL)isVerticalEdgeFormatLayoutOption: (NSLayoutFormatOptions)options
|
||||
{
|
||||
if (options & NSLayoutFormatAlignAllTop) {
|
||||
return YES;
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllBaseline) {
|
||||
return YES;
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllFirstBaseline) {
|
||||
return YES;
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllBottom) {
|
||||
return YES;
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllCenterY) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void)addViewSpacingConstraint: (NSNumber*)spacing previousView: (NSView*)previousView
|
||||
{
|
||||
CGFloat viewSpacingConstant = spacing ? [spacing doubleValue] : GS_DEFAULT_VIEW_SPACING;
|
||||
NSLayoutAttribute firstAttribute;
|
||||
NSLayoutAttribute secondAttribute;
|
||||
NSView *firstItem;
|
||||
NSView *secondItem;
|
||||
|
||||
NSLayoutFormatOptions directionOptions = _options & NSLayoutFormatDirectionMask;
|
||||
if (_isVerticalOrientation) {
|
||||
firstAttribute = NSLayoutAttributeTop;
|
||||
secondAttribute = NSLayoutAttributeBottom;
|
||||
firstItem = _view;
|
||||
secondItem = previousView;
|
||||
} else if (directionOptions & NSLayoutFormatDirectionRightToLeft) {
|
||||
firstAttribute = NSLayoutAttributeLeft;
|
||||
secondAttribute = NSLayoutAttributeRight;
|
||||
firstItem = previousView;
|
||||
secondItem = _view;
|
||||
} else if (directionOptions & NSLayoutFormatDirectionLeftToRight) {
|
||||
firstAttribute = NSLayoutAttributeLeft;
|
||||
secondAttribute = NSLayoutAttributeRight;
|
||||
firstItem = _view;
|
||||
secondItem = previousView;
|
||||
} else {
|
||||
firstAttribute = NSLayoutAttributeLeading;
|
||||
secondAttribute = NSLayoutAttributeTrailing;
|
||||
firstItem = _view;
|
||||
secondItem = previousView;
|
||||
}
|
||||
|
||||
NSLayoutConstraint *viewSeparatorConstraint = [NSLayoutConstraint constraintWithItem:firstItem attribute:firstAttribute relatedBy:NSLayoutRelationEqual toItem:secondItem attribute:secondAttribute multiplier:1.0 constant:viewSpacingConstant];
|
||||
|
||||
[_constraints addObject:viewSeparatorConstraint];
|
||||
}
|
||||
|
||||
-(void)addLeadingSuperviewConstraint: (NSNumber*)spacing
|
||||
{
|
||||
NSLayoutAttribute firstAttribute;
|
||||
NSView *firstItem;
|
||||
NSView *secondItem;
|
||||
|
||||
NSLayoutFormatOptions directionOptions = _options & NSLayoutFormatDirectionMask;
|
||||
if (_isVerticalOrientation) {
|
||||
firstAttribute = NSLayoutAttributeTop;
|
||||
firstItem = _view;
|
||||
secondItem = _view.superview;
|
||||
} else if (directionOptions & NSLayoutFormatDirectionRightToLeft) {
|
||||
firstAttribute = NSLayoutAttributeRight;
|
||||
firstItem = _view.superview;
|
||||
secondItem = _view;
|
||||
} else if (directionOptions & NSLayoutFormatDirectionLeftToRight) {
|
||||
firstAttribute = NSLayoutAttributeLeft;
|
||||
firstItem = _view;
|
||||
secondItem = _view.superview;
|
||||
} else {
|
||||
firstAttribute = _isVerticalOrientation ? NSLayoutAttributeTop : NSLayoutAttributeLeading;
|
||||
firstItem = _view;
|
||||
secondItem = _view.superview;
|
||||
}
|
||||
|
||||
CGFloat viewSpacingConstant = spacing ? [spacing doubleValue] : GS_DEFAULT_SUPERVIEW_SPACING;
|
||||
|
||||
NSLayoutConstraint *leadingConstraintToSuperview = [NSLayoutConstraint constraintWithItem:firstItem attribute:firstAttribute relatedBy:NSLayoutRelationEqual toItem:secondItem attribute:firstAttribute multiplier:1.0 constant:viewSpacingConstant];
|
||||
[_constraints addObject:leadingConstraintToSuperview];
|
||||
}
|
||||
|
||||
-(void)addTrailingToSuperviewConstraint: (NSNumber*)spacing
|
||||
{
|
||||
CGFloat viewSpacingConstant = spacing ? [spacing doubleValue] : GS_DEFAULT_SUPERVIEW_SPACING;
|
||||
|
||||
NSLayoutFormatOptions directionOptions = _options & NSLayoutFormatDirectionMask;
|
||||
NSLayoutAttribute attribute;
|
||||
NSView *firstItem;
|
||||
NSView *secondItem;
|
||||
|
||||
if (_isVerticalOrientation) {
|
||||
attribute = NSLayoutAttributeBottom;
|
||||
firstItem = _view.superview;
|
||||
secondItem = _view;
|
||||
} else if (directionOptions & NSLayoutFormatDirectionRightToLeft) {
|
||||
attribute = NSLayoutAttributeLeft;
|
||||
firstItem = _view;
|
||||
secondItem = _view.superview;
|
||||
} else if (directionOptions & NSLayoutFormatDirectionLeftToRight) {
|
||||
attribute = NSLayoutAttributeRight;
|
||||
firstItem = _view.superview;
|
||||
secondItem = _view;
|
||||
} else {
|
||||
attribute = NSLayoutAttributeTrailing;
|
||||
firstItem = _view.superview;
|
||||
secondItem = _view;
|
||||
}
|
||||
|
||||
NSLayoutConstraint *trailingConstraintToSuperview = [NSLayoutConstraint constraintWithItem: firstItem attribute:attribute relatedBy:NSLayoutRelationEqual toItem: secondItem attribute:attribute multiplier:1.0 constant:viewSpacingConstant];
|
||||
[_constraints addObject:trailingConstraintToSuperview];
|
||||
}
|
||||
|
||||
-(NSNumber*)parseLeadingSuperViewConnection
|
||||
{
|
||||
BOOL foundSuperview = [_scanner scanString:@"|" intoString:nil];
|
||||
if (!foundSuperview) {
|
||||
return nil;
|
||||
}
|
||||
_createLeadingConstraintToSuperview = YES;
|
||||
return [self parseConnection];
|
||||
}
|
||||
|
||||
-(NSNumber*)parseConnection
|
||||
{
|
||||
BOOL foundConnection = [_scanner scanString:@"-" intoString:nil];
|
||||
if (!foundConnection) {
|
||||
return [NSNumber numberWithDouble:0];
|
||||
}
|
||||
|
||||
NSNumber *simplePredicateValue = [self parseSimplePredicate];
|
||||
BOOL endConnectionFound = [_scanner scanString:@"-" intoString:nil];
|
||||
|
||||
if (simplePredicateValue != nil && !endConnectionFound) {
|
||||
[self failParseWithMessage:@"A connection must end with a '-'"];
|
||||
} else if (simplePredicateValue == nil && endConnectionFound) {
|
||||
[self failParseWithMessage:@"Found invalid connection"];
|
||||
}
|
||||
|
||||
return simplePredicateValue;
|
||||
}
|
||||
|
||||
-(NSNumber*)parseSimplePredicate
|
||||
{
|
||||
float constant;
|
||||
BOOL scanConstantResult = [_scanner scanFloat:&constant];
|
||||
if (scanConstantResult) {
|
||||
return [NSNumber numberWithDouble:constant];
|
||||
} else {
|
||||
NSString *metricName = nil;
|
||||
NSCharacterSet *simplePredicateTerminatorsCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"-[|"];
|
||||
BOOL didParseMetricName = [_scanner scanUpToCharactersFromSet:simplePredicateTerminatorsCharacterSet intoString:&metricName];
|
||||
if (!didParseMetricName) {
|
||||
return nil;
|
||||
}
|
||||
if (![self isValidIdentifer:metricName]) {
|
||||
[self failParseWithMessage:@"Invalid metric identifier. Metric identifiers must be a valid C identifier and may only contain letters, numbers and underscores"];
|
||||
}
|
||||
|
||||
NSNumber *metric = [self resolveMetricWithIdentifier:metricName];
|
||||
return metric;
|
||||
}
|
||||
}
|
||||
|
||||
-(NSArray*)parsePredicateList
|
||||
{
|
||||
BOOL startsWithPredicateList = [_scanner scanString:@"(" intoString:nil];
|
||||
if (!startsWithPredicateList) {
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
NSMutableArray *viewPredicateConstraints = [NSMutableArray array];
|
||||
BOOL shouldParsePredicate = YES;
|
||||
while (shouldParsePredicate) {
|
||||
GSObjectOfPredicate *predicate = [self parseObjectOfPredicate];
|
||||
[viewPredicateConstraints addObject:[self createConstraintFromParsedPredicate:predicate]];
|
||||
[self freeObjectOfPredicate:predicate];
|
||||
|
||||
shouldParsePredicate = [_scanner scanString:@"," intoString:nil];
|
||||
}
|
||||
|
||||
if (![_scanner scanString:@")" intoString:nil]) {
|
||||
[self failParseWithMessage:@"A predicate on a view must end with ')'"];
|
||||
}
|
||||
|
||||
return viewPredicateConstraints;
|
||||
}
|
||||
|
||||
-(NSLayoutConstraint*)createConstraintFromParsedPredicate: (GSObjectOfPredicate*)predicate
|
||||
{
|
||||
NSLayoutConstraint *constraint = nil;
|
||||
NSLayoutAttribute attribute = _isVerticalOrientation ? NSLayoutAttributeHeight : NSLayoutAttributeWidth;
|
||||
if (predicate->view != nil) {
|
||||
constraint = [NSLayoutConstraint constraintWithItem:_view attribute:attribute relatedBy:predicate->relation toItem:predicate->view attribute:attribute multiplier:1.0 constant:predicate->constant];
|
||||
} else {
|
||||
constraint = [NSLayoutConstraint constraintWithItem:_view attribute:attribute relatedBy:predicate->relation toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:predicate->constant];
|
||||
}
|
||||
|
||||
if (predicate->priority) {
|
||||
constraint.priority = [predicate->priority doubleValue];
|
||||
}
|
||||
|
||||
return constraint;
|
||||
}
|
||||
|
||||
-(GSObjectOfPredicate*)parseObjectOfPredicate
|
||||
{
|
||||
NSLayoutRelation relation = [self parseRelation];
|
||||
|
||||
CGFloat parsedConstant;
|
||||
NSView *predicatedView = nil;
|
||||
BOOL scanConstantResult = [_scanner scanDouble:&parsedConstant];
|
||||
if (!scanConstantResult) {
|
||||
NSString *identiferName = [self parseIdentifier];
|
||||
if (![self isValidIdentifer:identiferName]) {
|
||||
[self failParseWithMessage:@"Invalid metric or view identifier. Metric/View identifiers must be a valid C identifier and may only contain letters, numbers and underscores"];
|
||||
}
|
||||
|
||||
NSNumber *metric = [_metrics objectForKey:identiferName];
|
||||
if (metric != nil) {
|
||||
parsedConstant = [metric doubleValue];
|
||||
} else if ([_views objectForKey:identiferName]) {
|
||||
parsedConstant = 0;
|
||||
predicatedView = [_views objectForKey:identiferName];
|
||||
} else {
|
||||
NSString *message = [NSString stringWithFormat:@"Failed to find constant or metric for identifier '%@'", identiferName];
|
||||
[self failParseWithMessage:message];
|
||||
}
|
||||
}
|
||||
|
||||
NSNumber *priorityValue = [self parsePriority];
|
||||
|
||||
GSObjectOfPredicate *predicate = calloc(1, sizeof(GSObjectOfPredicate));
|
||||
predicate->priority = priorityValue;
|
||||
predicate->relation = relation;
|
||||
predicate->constant = parsedConstant;
|
||||
predicate->view = predicatedView;
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
-(NSLayoutRelation)parseRelation
|
||||
{
|
||||
if ([_scanner scanString:@"==" intoString:nil]) {
|
||||
return NSLayoutRelationEqual;
|
||||
} else if ([_scanner scanString:@">=" intoString:nil]) {
|
||||
return NSLayoutRelationGreaterThanOrEqual;
|
||||
} else if ([_scanner scanString:@"<=" intoString:nil]) {
|
||||
return NSLayoutRelationLessThanOrEqual;
|
||||
} else {
|
||||
return NSLayoutRelationEqual;
|
||||
}
|
||||
}
|
||||
|
||||
-(NSNumber*)parsePriority
|
||||
{
|
||||
NSCharacterSet *priorityMarkerCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"@"];
|
||||
BOOL foundPriorityMarker = [_scanner scanCharactersFromSet:priorityMarkerCharacterSet intoString:nil];
|
||||
if (!foundPriorityMarker) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [self parseConstant];
|
||||
}
|
||||
|
||||
-(NSNumber*)resolveMetricWithIdentifier: (NSString*)identifier
|
||||
{
|
||||
NSNumber *metric = [_metrics objectForKey:identifier];
|
||||
if (metric == nil) {
|
||||
[self failParseWithMessage:@"Found metric not inside metric dictionary"];
|
||||
}
|
||||
return metric;
|
||||
}
|
||||
|
||||
-(NSView*)resolveViewWithIdentifier: (NSString*)identifier
|
||||
{
|
||||
NSView *view = [_views objectForKey:identifier];
|
||||
if (view == nil) {
|
||||
[self failParseWithMessage:@"Found view not inside view dictionary"];
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
-(NSNumber*)parseConstant
|
||||
{
|
||||
CGFloat constant;
|
||||
BOOL scanConstantResult = [_scanner scanDouble:&constant];
|
||||
if (scanConstantResult) {
|
||||
return [NSNumber numberWithFloat:constant];
|
||||
}
|
||||
|
||||
NSString *metricName = [self parseIdentifier];
|
||||
if (![self isValidIdentifer:metricName]) {
|
||||
[self failParseWithMessage:@"Invalid metric identifier. Metric identifiers must be a valid C identifier and may only contain letters, numbers and underscores"];
|
||||
}
|
||||
|
||||
return [self resolveMetricWithIdentifier:metricName];
|
||||
}
|
||||
|
||||
-(NSString*)parseIdentifier
|
||||
{
|
||||
NSString *identifierName = nil;
|
||||
NSCharacterSet *identifierTerminators = [NSCharacterSet characterSetWithCharactersInString:@"),"];
|
||||
BOOL scannedIdentifier = [_scanner scanUpToCharactersFromSet:identifierTerminators intoString:&identifierName];
|
||||
if (!scannedIdentifier) {
|
||||
[self failParseWithMessage:@"Failed to find constant or metric"];
|
||||
}
|
||||
|
||||
return identifierName;
|
||||
}
|
||||
|
||||
-(void)parseViewOpen
|
||||
{
|
||||
NSCharacterSet *openViewIdentifier = [NSCharacterSet characterSetWithCharactersInString:@"["];
|
||||
BOOL scannedOpenBracket = [_scanner scanCharactersFromSet:openViewIdentifier intoString:nil];
|
||||
if (!scannedOpenBracket) {
|
||||
[[NSException exceptionWithName:NSInternalInconsistencyException reason:@"A view must start with a '['" userInfo:nil] raise];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)parseViewClose
|
||||
{
|
||||
NSCharacterSet *closeViewIdentifier = [NSCharacterSet characterSetWithCharactersInString:@"]"];
|
||||
BOOL scannedCloseBracket = [_scanner scanCharactersFromSet:closeViewIdentifier intoString:nil];
|
||||
if (!scannedCloseBracket) {
|
||||
[[NSException exceptionWithName:NSInternalInconsistencyException reason:@"A view must end with a ']'" userInfo:nil] raise];
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL)isValidIdentifer: (NSString*)identifer
|
||||
{
|
||||
NSRegularExpression *cIdentifierRegex = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z_][a-zA-Z0-9_]*$" options:0 error:nil];
|
||||
NSArray *matches = [cIdentifierRegex matchesInString:identifer options:0 range:NSMakeRange(0, identifer.length)];
|
||||
|
||||
return [matches count] > 0;
|
||||
}
|
||||
|
||||
-(NSArray*)layoutAttributesForLayoutFormatOptions: (NSLayoutFormatOptions)options {
|
||||
NSMutableArray *attributes = [NSMutableArray array];
|
||||
|
||||
if (options & NSLayoutFormatAlignAllLeft) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeLeft]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllRight) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeRight]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllTop) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeTop]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllBottom) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeBottom]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllLeading) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeLeading]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllTrailing) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeTrailing]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllCenterX) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeCenterX]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllCenterY) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeCenterY]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllBaseline) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeBaseline]];
|
||||
}
|
||||
if (options & NSLayoutFormatAlignAllFirstBaseline) {
|
||||
[attributes addObject:[NSNumber numberWithInteger:NSLayoutAttributeFirstBaseline]];
|
||||
}
|
||||
|
||||
if ([attributes count] == 0) {
|
||||
[self failParseWithMessage:@"Unrecognized layout formatting option"];
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
-(void)failParseWithMessage: (NSString*)parseErrorMessage
|
||||
{
|
||||
NSException *parseException = [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Unable to parse constraint format: %@", parseErrorMessage] userInfo:nil];
|
||||
[parseException raise];
|
||||
}
|
||||
|
||||
|
||||
-(void)freeObjectOfPredicate: (GSObjectOfPredicate*)predicate
|
||||
{
|
||||
predicate->view = nil;
|
||||
predicate->priority = nil;
|
||||
free(predicate);
|
||||
}
|
||||
|
||||
@end
|
|
@ -32,6 +32,7 @@
|
|||
#import "AppKit/NSLayoutConstraint.h"
|
||||
#import "AppKit/NSWindow.h"
|
||||
#import "AppKit/NSApplication.h"
|
||||
#import "GSAutoLayoutVFLParser.h"
|
||||
|
||||
static NSMutableArray *activeConstraints = nil;
|
||||
// static NSNotificationCenter *nc = nil;
|
||||
|
@ -236,8 +237,16 @@ static NSMutableArray *activeConstraints = nil;
|
|||
metrics: (NSDictionary *)metrics
|
||||
views: (NSDictionary *)views
|
||||
{
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity: 10];
|
||||
return array;
|
||||
GSAutoLayoutVFLParser *parser = [[GSAutoLayoutVFLParser alloc]
|
||||
initWithFormat: fmt
|
||||
options: opt
|
||||
metrics: metrics
|
||||
views: views];
|
||||
NSArray *constraints = [parser parse];
|
||||
|
||||
[parser release];
|
||||
|
||||
return constraints;
|
||||
}
|
||||
|
||||
- (instancetype) initWithItem: (id)firstItem
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue