mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 15:00:56 +00:00
1294 lines
47 KiB
Objective-C
1294 lines
47 KiB
Objective-C
/* Copyright (C) 2023 Free Software Foundation, Inc.
|
|
|
|
By: Benjamin Johnson
|
|
Date: 28-2-2023
|
|
This file is part of the GNUstep Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110 USA.
|
|
*/
|
|
|
|
#import "GSAutoLayoutEngine.h"
|
|
#import "AppKit/NSLayoutConstraint.h"
|
|
#import "GSCSConstraint.h"
|
|
#import "GSFastEnumeration.h"
|
|
#import "NSViewPrivate.h"
|
|
|
|
enum
|
|
{
|
|
GSLayoutAttributeNotAnAttribute = 0,
|
|
GSLayoutAttributeLeft = 1,
|
|
GSLayoutAttributeRight,
|
|
GSLayoutAttributeTop,
|
|
GSLayoutAttributeBottom,
|
|
GSLayoutAttributeLeading,
|
|
GSLayoutAttributeTrailing,
|
|
GSLayoutAttributeWidth,
|
|
GSLayoutAttributeHeight,
|
|
GSLayoutAttributeCenterX,
|
|
GSLayoutAttributeCenterY,
|
|
GSLayoutAttributeLastBaseline,
|
|
GSLayoutAttributeBaseline = GSLayoutAttributeLastBaseline,
|
|
GSLayoutAttributeFirstBaseline,
|
|
GSLayoutAttributeMinX = 32,
|
|
GSLayoutAttributeMinY = 33,
|
|
GSLayoutAttributeMaxX = 36,
|
|
GSLayoutAttributeMaxY = 37
|
|
};
|
|
typedef NSInteger GSLayoutAttribute;
|
|
|
|
enum
|
|
{
|
|
GSLayoutViewAttributeBaselineOffsetFromBottom = 1,
|
|
GSLayoutViewAttributeFirstBaselineOffsetFromTop,
|
|
GSLayoutViewAttributeIntrinsicWidth,
|
|
GSLayoutViewAttributeIntrinsicHeight,
|
|
} GSLayoutViewAttribue;
|
|
typedef NSInteger GSLayoutViewAttribute;
|
|
|
|
@interface GSAutoLayoutEngine (PrivateMethods)
|
|
|
|
- (GSCSConstraint *) solverConstraintForConstraint:
|
|
(NSLayoutConstraint *)constraint;
|
|
|
|
- (void) mapLayoutConstraint: (NSLayoutConstraint *)layoutConstraint
|
|
toSolverConstraint: (GSCSConstraint *)solverConstraint;
|
|
|
|
- (void) updateAlignmentRectsForTrackedViews;
|
|
|
|
- (void) updateAlignmentRectsForTrackedViewsForSolution:
|
|
(GSCSSolution *)solution;
|
|
|
|
- (BOOL) hasConstraintsForView: (NSView *)view;
|
|
|
|
- (GSCSConstraint *) getExistingConstraintForAutolayoutConstraint:
|
|
(NSLayoutConstraint *)constraint;
|
|
|
|
- (void) addConstraintAgainstViewConstraintsArray:
|
|
(NSLayoutConstraint *)constraint;
|
|
|
|
- (void) removeConstraintAgainstViewConstraintsArray:
|
|
(NSLayoutConstraint *)constraint;
|
|
|
|
- (void) addSupportingInternalConstraintsToView: (NSView *)view
|
|
forAttribute: (NSLayoutAttribute)attribute
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) removeInternalConstraintsForView: (NSView *)view;
|
|
|
|
- (void) addSolverConstraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) removeSolverConstraint: (GSCSConstraint *)constraint;
|
|
|
|
- (NSArray *) constraintsForView: (NSView *)view;
|
|
|
|
- (NSNumber *) indexForView: (NSView *)view;
|
|
|
|
- (void) notifyViewsOfAlignmentRectChange: (NSArray *)viewsWithChanges;
|
|
|
|
- (BOOL) solverCanSolveAlignmentRectForView: (NSView *)view
|
|
solution: (GSCSSolution *)solution;
|
|
|
|
- (void) recordAlignmentRect: (NSRect)alignmentRect
|
|
forViewIndex: (NSNumber *)viewIndex;
|
|
|
|
- (NSRect) solverAlignmentRectForView: (NSView *)view
|
|
solution: (GSCSSolution *)solution;
|
|
|
|
- (NSRect) currentAlignmentRectForViewAtIndex: (NSNumber *)viewIndex;
|
|
|
|
- (BOOL) updateViewAligmentRect: (GSCSSolution *)solution view: (NSView *)view;
|
|
|
|
- (BOOL) hasAddedWidthAndHeightConstraintsToView: (NSView *)view;
|
|
|
|
- (GSCSVariable *) variableForView: (NSView *)view
|
|
andAttribute: (GSLayoutAttribute)attribute;
|
|
|
|
- (GSCSVariable *) variableForView: (NSView *)view
|
|
andViewAttribute: (GSLayoutViewAttribute)attribute;
|
|
|
|
- (void) resolveVariableForView: (NSView *)view
|
|
attribute: (GSLayoutViewAttribute)attribute;
|
|
|
|
- (GSCSVariable *) createVariableWithName: (NSString *)name;
|
|
|
|
- (GSCSVariable *) getExistingVariableForView: (NSView *)view
|
|
withAttribute: (GSLayoutAttribute)attribute;
|
|
|
|
- (GSCSVariable *) getExistingVariableForView: (NSView *)view
|
|
withViewAttribute:
|
|
(GSLayoutViewAttribute)attribute;
|
|
|
|
- (GSCSVariable *) variableWithName: (NSString *)variableName;
|
|
|
|
- (NSString *) getVariableIdentifierForView: (NSView *)view
|
|
withAttribute: (GSLayoutAttribute)attribute;
|
|
|
|
- (NSString *) getAttributeName: (GSLayoutAttribute)attribute;
|
|
|
|
- (NSString *) getLayoutViewAttributeName: (GSLayoutViewAttribute)attribute;
|
|
|
|
- (NSString *) getDynamicVariableIdentifierForView: (NSView *)view
|
|
withViewAttribute:
|
|
(GSLayoutViewAttribute)attribute;
|
|
|
|
- (NSString *) getIdentifierForView: (NSView *)view;
|
|
|
|
- (NSInteger) registerView: (NSView *)view;
|
|
|
|
- (void) addInternalWidthConstraintForView: (NSView *)view;
|
|
|
|
- (void) addInternalHeightConstraintForView: (NSView *)view;
|
|
|
|
- (void) addInternalLeadingConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalTrailingConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalLeftConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalRightConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalBottomConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalTopConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalCenterXConstraintsForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalCenterYConstraintsForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalFirstBaselineConstraintsForView: (NSView *)view
|
|
constraint:
|
|
(GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalBaselineConstraintsForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint;
|
|
|
|
- (void) addInternalSolverConstraint: (GSCSConstraint *)constraint
|
|
forView: (NSView *)view;
|
|
|
|
- (void) addIntrinsicContentSizeConstraintsToView: (NSView *)view;
|
|
|
|
- (void) addSupportingIntrinsicSizeConstraintsToView: (NSView *)view
|
|
orientation:
|
|
(NSLayoutConstraintOrientation)
|
|
orientation
|
|
intrinsicSizeAttribute:
|
|
(GSLayoutViewAttribute)intrinsicSizeAttribute
|
|
dimensionAttribute:
|
|
(GSLayoutAttribute)dimensionAttribute;
|
|
|
|
- (void) addSupportingConstraintForLayoutViewAttribute:
|
|
(GSLayoutViewAttribute)attribute
|
|
view: (NSView *)view
|
|
constraint:
|
|
(GSCSConstraint *)constraint;
|
|
|
|
- (GSCSConstraint *) solverConstraintForRelationalConstraint:
|
|
(NSLayoutConstraint *)constraint;
|
|
|
|
- (GSCSConstraint *) solverConstraintForNonRelationalConstraint:
|
|
(NSLayoutConstraint *)constraint;
|
|
|
|
- (void) addObserverToConstraint: (NSLayoutConstraint *)constranit;
|
|
|
|
- (void) observeValueForKeyPath: (NSString *)keyPath
|
|
ofObject: (id)object
|
|
change: (NSDictionary *)change
|
|
context: (void *)context;
|
|
|
|
- (void) updateConstraint: (NSLayoutConstraint *)constraint;
|
|
|
|
- (void) addSupportingSolverConstraint: (GSCSConstraint *)supportingConstraint
|
|
forSolverConstraint: (GSCSConstraint *)constraint;
|
|
|
|
- (CGFloat) valueForView: (NSView *)view
|
|
attribute: (GSLayoutViewAttribute)attribute;
|
|
|
|
- (int) getConstantMultiplierForLayoutAttribute: (NSLayoutAttribute)attribute;
|
|
|
|
@end
|
|
|
|
@implementation GSAutoLayoutEngine
|
|
|
|
- (instancetype) initWithSolver: (GSCassowarySolver *)cassowarySolver;
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
ASSIGN(_solver, cassowarySolver);
|
|
|
|
ASSIGN(_variablesByKey, [NSMapTable strongToStrongObjectsMapTable]);
|
|
|
|
ASSIGN(_constraintsByAutoLayoutConstaintHash, [NSMapTable strongToStrongObjectsMapTable]);
|
|
|
|
ASSIGN(_layoutConstraintsBySolverConstraint, [NSMapTable strongToStrongObjectsMapTable]);
|
|
|
|
ASSIGN(_solverConstraints, [NSMutableArray array]);
|
|
|
|
ASSIGN(_trackedViews, [NSMutableArray array]);
|
|
|
|
ASSIGN(_supportingConstraintsByConstraint, [NSMapTable strongToStrongObjectsMapTable]);
|
|
|
|
ASSIGN(_viewAlignmentRectByViewIndex, [NSMutableDictionary dictionary]);
|
|
|
|
ASSIGN(_viewIndexByViewHash, [NSMutableDictionary dictionary]);
|
|
|
|
ASSIGN(_constraintsByViewIndex, [NSMutableDictionary dictionary]);
|
|
|
|
ASSIGN(_internalConstraintsByViewIndex, [NSMapTable strongToStrongObjectsMapTable]);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype) init
|
|
{
|
|
GSCassowarySolver *solver = [[GSCassowarySolver alloc] init];
|
|
self = [self initWithSolver: solver];
|
|
RELEASE(solver);
|
|
return self;
|
|
}
|
|
|
|
- (void) addConstraints: (NSArray *)constraints
|
|
{
|
|
FOR_IN(NSLayoutConstraint *, constraint, constraints)
|
|
[self addConstraint: constraint];
|
|
END_FOR_IN(constraints);
|
|
}
|
|
|
|
- (void) addConstraint: (NSLayoutConstraint *)constraint
|
|
{
|
|
GSCSConstraint *solverConstraint =
|
|
[self solverConstraintForConstraint: constraint];
|
|
|
|
[self mapLayoutConstraint: constraint toSolverConstraint: solverConstraint];
|
|
|
|
[self addSupportingInternalConstraintsToView: [constraint firstItem]
|
|
forAttribute: [constraint firstAttribute]
|
|
constraint: solverConstraint];
|
|
|
|
if ([constraint secondItem])
|
|
{
|
|
[self
|
|
addSupportingInternalConstraintsToView: [constraint secondItem]
|
|
forAttribute: [constraint secondAttribute]
|
|
constraint: solverConstraint];
|
|
}
|
|
|
|
[self addSolverConstraint: solverConstraint];
|
|
[self updateAlignmentRectsForTrackedViews];
|
|
|
|
[self addConstraintAgainstViewConstraintsArray: constraint];
|
|
}
|
|
|
|
- (void) removeConstraint: (NSLayoutConstraint *)constraint
|
|
{
|
|
GSCSConstraint *solverConstraint =
|
|
[self getExistingConstraintForAutolayoutConstraint: constraint];
|
|
if (solverConstraint == nil)
|
|
{
|
|
return;
|
|
}
|
|
|
|
[self removeSolverConstraint: solverConstraint];
|
|
|
|
NSArray *internalConstraints =
|
|
[_supportingConstraintsByConstraint objectForKey: solverConstraint];
|
|
if (internalConstraints)
|
|
{
|
|
FOR_IN(GSCSConstraint *, internalConstraint, internalConstraints)
|
|
[self removeSolverConstraint: internalConstraint];
|
|
END_FOR_IN(internalConstraints);
|
|
}
|
|
|
|
[_supportingConstraintsByConstraint setObject: nil forKey: solverConstraint];
|
|
|
|
[self updateAlignmentRectsForTrackedViews];
|
|
[self removeConstraintAgainstViewConstraintsArray: constraint];
|
|
|
|
if ([self hasConstraintsForView: [constraint firstItem]])
|
|
{
|
|
[self removeInternalConstraintsForView: [constraint firstItem]];
|
|
}
|
|
if ([constraint secondItem] != nil &&
|
|
[self hasConstraintsForView: [constraint secondItem]])
|
|
{
|
|
[self removeInternalConstraintsForView: [constraint secondItem]];
|
|
}
|
|
}
|
|
|
|
- (void) removeConstraints: (NSArray *)constraints
|
|
{
|
|
FOR_IN(NSLayoutConstraint *, constraint, constraints)
|
|
[self removeConstraint: constraint];
|
|
END_FOR_IN(constraints);
|
|
}
|
|
|
|
- (GSCSConstraint *) solverConstraintForConstraint:
|
|
(NSLayoutConstraint *)constraint
|
|
{
|
|
if ([constraint secondItem] == nil)
|
|
{
|
|
return [self solverConstraintForNonRelationalConstraint: constraint];
|
|
}
|
|
else
|
|
{
|
|
return [self solverConstraintForRelationalConstraint: constraint];
|
|
}
|
|
}
|
|
|
|
- (GSCSConstraint *) solverConstraintForNonRelationalConstraint:
|
|
(NSLayoutConstraint *)constraint
|
|
{
|
|
GSCSVariable *firstItemConstraintVariable =
|
|
[self variableForView: [constraint firstItem]
|
|
andAttribute: (GSLayoutAttribute)[constraint firstAttribute]];
|
|
GSCSConstraint *newConstraint;
|
|
switch ([constraint relation])
|
|
{
|
|
case NSLayoutRelationLessThanOrEqual:
|
|
newConstraint = [GSCSConstraint constraintWithLeftVariable: firstItemConstraintVariable operator: GSCSConstraintOperatorLessThanOrEqual rightConstant: [constraint constant]];
|
|
break;
|
|
case NSLayoutRelationEqual:
|
|
default:
|
|
newConstraint = [GSCSConstraint constraintWithLeftVariable: firstItemConstraintVariable operator: GSCSConstraintOperatorEqual rightConstant: [constraint constant]];
|
|
break;
|
|
case NSLayoutRelationGreaterThanOrEqual:
|
|
newConstraint = [GSCSConstraint constraintWithLeftVariable: firstItemConstraintVariable operator: GSCSConstraintOperationGreaterThanOrEqual rightConstant: [constraint constant]];
|
|
break;
|
|
}
|
|
GSCSStrength *strength =
|
|
[[GSCSStrength alloc] initWithName: nil strength: [constraint priority]];
|
|
[newConstraint setStrength: strength];
|
|
RELEASE(strength);
|
|
|
|
return newConstraint;
|
|
}
|
|
|
|
- (GSCSConstraint *) solverConstraintForRelationalConstraint:
|
|
(NSLayoutConstraint *)constraint
|
|
{
|
|
GSCSVariable *firstItemConstraintVariable =
|
|
[self variableForView: [constraint firstItem]
|
|
andAttribute: (GSLayoutAttribute)[constraint firstAttribute]];
|
|
GSCSVariable *secondItemConstraintVariable =
|
|
[self variableForView: [constraint secondItem]
|
|
andAttribute: (GSLayoutAttribute)[constraint secondAttribute]];
|
|
|
|
CGFloat multiplier = [constraint multiplier];
|
|
|
|
GSCSConstraintOperator op = GSCSConstraintOperatorEqual;
|
|
switch ([constraint relation])
|
|
{
|
|
case NSLayoutRelationEqual:
|
|
op = GSCSConstraintOperatorEqual;
|
|
break;
|
|
case NSLayoutRelationLessThanOrEqual:
|
|
op = GSCSConstraintOperatorLessThanOrEqual;
|
|
break;
|
|
case NSLayoutRelationGreaterThanOrEqual:
|
|
op = GSCSConstraintOperationGreaterThanOrEqual;
|
|
break;
|
|
}
|
|
double constant =
|
|
[self getConstantMultiplierForLayoutAttribute: [constraint secondAttribute]] * [constraint constant];
|
|
|
|
GSCSLinearExpression *rightExpression = [[GSCSLinearExpression alloc]
|
|
initWithVariable: secondItemConstraintVariable
|
|
coefficient: multiplier
|
|
constant: constant];
|
|
GSCSConstraint *newConstraint = [GSCSConstraint constraintWithLeftVariable: firstItemConstraintVariable operator: op rightExpression: rightExpression];
|
|
RELEASE(rightExpression);
|
|
|
|
GSCSStrength *strength =
|
|
[[GSCSStrength alloc] initWithName: nil strength: [constraint priority]];
|
|
[newConstraint setStrength: strength];
|
|
RELEASE(strength);
|
|
|
|
return newConstraint;
|
|
}
|
|
|
|
- (int) getConstantMultiplierForLayoutAttribute: (NSLayoutAttribute)attribute
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case NSLayoutAttributeTop:
|
|
return -1;
|
|
case NSLayoutAttributeBottom:
|
|
return 1;
|
|
case NSLayoutAttributeLeading:
|
|
return 1;
|
|
case NSLayoutAttributeTrailing:
|
|
return -1;
|
|
case NSLayoutAttributeLeft:
|
|
return 1;
|
|
case NSLayoutAttributeRight:
|
|
return -1;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
- (GSCSVariable *) variableForView: (NSView *)view
|
|
andViewAttribute: (GSLayoutViewAttribute)attribute
|
|
{
|
|
NSString *variableName = [self getDynamicVariableIdentifierForView: view
|
|
withViewAttribute: attribute];
|
|
|
|
GSCSVariable *existingVariable = [self variableWithName: variableName];
|
|
if (existingVariable != nil)
|
|
{
|
|
return existingVariable;
|
|
}
|
|
else
|
|
{
|
|
return [self createVariableWithName: variableName];
|
|
}
|
|
}
|
|
|
|
- (GSCSVariable *) variableForView: (NSView *)view
|
|
andAttribute: (GSLayoutAttribute)attribute
|
|
{
|
|
NSString *variableName =
|
|
[self getVariableIdentifierForView: view withAttribute: attribute];
|
|
GSCSVariable *existingVariable = [self variableWithName: variableName];
|
|
if (existingVariable != nil)
|
|
{
|
|
return existingVariable;
|
|
}
|
|
else
|
|
{
|
|
return [self createVariableWithName: variableName];
|
|
}
|
|
}
|
|
|
|
- (GSCSVariable *) getExistingVariableForView: (NSView *)view
|
|
withAttribute: (GSLayoutAttribute)attribute
|
|
{
|
|
NSString *variableIdentifier =
|
|
[self getVariableIdentifierForView: view
|
|
withAttribute: (GSLayoutAttribute)attribute];
|
|
return [self variableWithName: variableIdentifier];
|
|
}
|
|
|
|
- (GSCSVariable *) createVariableWithName: (NSString *)name
|
|
{
|
|
GSCSVariable *variable = [GSCSVariable variableWithValue: 0 name: name];
|
|
[_variablesByKey setObject: variable forKey: name];
|
|
|
|
return variable;
|
|
}
|
|
|
|
- (GSCSVariable *) getExistingVariableForView: (NSView *)view
|
|
withViewAttribute: (GSLayoutViewAttribute)attribute
|
|
{
|
|
NSString *variableIdentifier =
|
|
[self getDynamicVariableIdentifierForView: view
|
|
withViewAttribute: attribute];
|
|
return [self variableWithName: variableIdentifier];
|
|
}
|
|
|
|
- (GSCSVariable *) variableWithName: (NSString *)variableName
|
|
{
|
|
return [_variablesByKey objectForKey: variableName];
|
|
}
|
|
|
|
- (NSString *) getVariableIdentifierForView: (NSView *)view
|
|
withAttribute: (GSLayoutAttribute)attribute
|
|
{
|
|
NSString *viewIdentifier = [self getIdentifierForView: view];
|
|
return [NSString stringWithFormat: @"%@.%@", viewIdentifier,
|
|
[self getAttributeName: attribute]];
|
|
}
|
|
|
|
- (NSString *) getAttributeName: (GSLayoutAttribute)attribute
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case GSLayoutAttributeTop:
|
|
return @"top";
|
|
case GSLayoutAttributeBottom:
|
|
return @"bottom";
|
|
case GSLayoutAttributeLeading:
|
|
return @"leading";
|
|
case GSLayoutAttributeLeft:
|
|
return @"left";
|
|
case GSLayoutAttributeRight:
|
|
return @"right";
|
|
case GSLayoutAttributeTrailing:
|
|
return @"trailing";
|
|
case GSLayoutAttributeHeight:
|
|
return @"height";
|
|
case GSLayoutAttributeWidth:
|
|
return @"width";
|
|
case GSLayoutAttributeCenterX:
|
|
return @"centerX";
|
|
case GSLayoutAttributeCenterY:
|
|
return @"centerY";
|
|
case GSLayoutAttributeBaseline:
|
|
return @"baseline";
|
|
case GSLayoutAttributeFirstBaseline:
|
|
return @"firstBaseline";
|
|
case GSLayoutAttributeMinX:
|
|
return @"minX";
|
|
case GSLayoutAttributeMinY:
|
|
return @"minY";
|
|
case GSLayoutAttributeMaxX:
|
|
return @"maxX";
|
|
case GSLayoutAttributeMaxY:
|
|
return @"maxY";
|
|
default:
|
|
[[NSException exceptionWithName: @"Not handled"
|
|
reason: @"GSLayoutAttribute not handled"
|
|
userInfo: nil] raise];
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (NSString *) getLayoutViewAttributeName: (GSLayoutViewAttribute)attribute
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case GSLayoutViewAttributeBaselineOffsetFromBottom:
|
|
return @"baselineOffsetFromBottom";
|
|
case GSLayoutViewAttributeFirstBaselineOffsetFromTop:
|
|
return @"firstBaselineOffsetFromTop";
|
|
case GSLayoutViewAttributeIntrinsicWidth:
|
|
return @"intrinsicContentSize.width";
|
|
case GSLayoutViewAttributeIntrinsicHeight:
|
|
return @"intrinsicContentSize.height";
|
|
default:
|
|
[[NSException
|
|
exceptionWithName: @"GSLayoutViewAttribute Not handled"
|
|
reason: @"The provided GSLayoutViewAttribute does "
|
|
@"not have a name"
|
|
userInfo: nil] raise];
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (NSString *) getDynamicVariableIdentifierForView: (NSView *)view
|
|
withViewAttribute:
|
|
(GSLayoutViewAttribute)attribute
|
|
{
|
|
NSString *viewIdentifier = [self getIdentifierForView: view];
|
|
return
|
|
[NSString stringWithFormat: @"%@.%@", viewIdentifier,
|
|
[self getLayoutViewAttributeName: attribute]];
|
|
}
|
|
|
|
- (NSString *) getIdentifierForView: (NSView *)view
|
|
{
|
|
NSUInteger viewIndex;
|
|
NSNumber *existingViewIndex = [self indexForView: view];
|
|
if (existingViewIndex)
|
|
{
|
|
viewIndex = [existingViewIndex unsignedIntegerValue];
|
|
}
|
|
else
|
|
{
|
|
viewIndex = [self registerView: view];
|
|
}
|
|
|
|
return [NSString stringWithFormat: @"view%ld", (long)viewIndex];
|
|
}
|
|
|
|
- (NSInteger) registerView: (NSView *)view
|
|
{
|
|
NSUInteger viewIndex = [_trackedViews count];
|
|
[_trackedViews addObject: view];
|
|
[_viewIndexByViewHash
|
|
setObject: [NSNumber numberWithUnsignedInteger: viewIndex]
|
|
forKey: [NSNumber numberWithUnsignedInteger: [view hash]]];
|
|
|
|
return viewIndex;
|
|
}
|
|
|
|
- (void) mapLayoutConstraint: (NSLayoutConstraint *)layoutConstraint
|
|
toSolverConstraint: (GSCSConstraint *)solverConstraint
|
|
{
|
|
[_constraintsByAutoLayoutConstaintHash setObject: solverConstraint
|
|
forKey: layoutConstraint];
|
|
[_layoutConstraintsBySolverConstraint setObject: layoutConstraint
|
|
forKey: solverConstraint];
|
|
}
|
|
|
|
- (void) addSupportingInternalConstraintsToView: (NSView *)view
|
|
forAttribute: (NSLayoutAttribute)attribute
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
if (![self hasAddedWidthAndHeightConstraintsToView: view])
|
|
{
|
|
[self addInternalWidthConstraintForView: view];
|
|
[self addInternalHeightConstraintForView: view];
|
|
[self addIntrinsicContentSizeConstraintsToView: view];
|
|
}
|
|
|
|
switch (attribute)
|
|
{
|
|
case NSLayoutAttributeTrailing:
|
|
[self addInternalTrailingConstraintForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeLeading:
|
|
[self addInternalLeadingConstraintForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeLeft:
|
|
[self addInternalLeftConstraintForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeRight:
|
|
[self addInternalRightConstraintForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeTop:
|
|
[self addInternalTopConstraintForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeBottom:
|
|
[self addInternalBottomConstraintForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeCenterX:
|
|
[self addInternalCenterXConstraintsForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeCenterY:
|
|
[self addInternalCenterYConstraintsForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeBaseline:
|
|
[self addInternalBaselineConstraintsForView: view
|
|
constraint: constraint];
|
|
break;
|
|
case NSLayoutAttributeFirstBaseline:
|
|
[self addInternalFirstBaselineConstraintsForView: view
|
|
constraint: constraint];
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (BOOL) hasAddedWidthAndHeightConstraintsToView: (NSView *)view
|
|
{
|
|
NSArray *added = [_internalConstraintsByViewIndex objectForKey: view];
|
|
return added != nil;
|
|
}
|
|
|
|
- (void) addInternalWidthConstraintForView: (NSView *)view
|
|
{
|
|
GSCSVariable *widthConstraintVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeWidth];
|
|
GSCSVariable *minX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinX];
|
|
GSCSVariable *maxX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMaxX];
|
|
|
|
GSCSLinearExpression *maxXMinusMinX =
|
|
[[GSCSLinearExpression alloc] initWithVariable: maxX];
|
|
[maxXMinusMinX addVariable: minX coefficient: -1];
|
|
GSCSConstraint *widthRelationshipToMaxXAndMinXConstraint = [GSCSConstraint constraintWithLeftVariable: widthConstraintVariable operator: GSCSConstraintOperatorEqual rightExpression: maxXMinusMinX];
|
|
|
|
[self addInternalSolverConstraint: widthRelationshipToMaxXAndMinXConstraint
|
|
forView: view];
|
|
RELEASE(maxXMinusMinX);
|
|
}
|
|
|
|
- (void) addInternalHeightConstraintForView: (NSView *)view
|
|
{
|
|
GSCSVariable *heightConstraintVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeHeight];
|
|
GSCSVariable *minY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinY];
|
|
GSCSVariable *maxY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMaxY];
|
|
|
|
GSCSLinearExpression *maxYMinusMinY =
|
|
[[GSCSLinearExpression alloc] initWithVariable: maxY];
|
|
[maxYMinusMinY addVariable: minY coefficient: -1];
|
|
GSCSConstraint *heightConstraint = [GSCSConstraint constraintWithLeftVariable: heightConstraintVariable operator: GSCSConstraintOperatorEqual rightExpression: maxYMinusMinY];
|
|
[self addInternalSolverConstraint: heightConstraint forView: view];
|
|
|
|
RELEASE(maxYMinusMinY);
|
|
}
|
|
|
|
- (void) addInternalLeadingConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *minX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinX];
|
|
GSCSVariable *leadingVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeLeading];
|
|
GSCSConstraint *minXLeadingRelationshipConstraint = [GSCSConstraint constraintWithLeftVariable: minX operator: GSCSConstraintOperatorEqual rightVariable: leadingVariable];
|
|
[self addSupportingSolverConstraint: minXLeadingRelationshipConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalTrailingConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *trailingVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeTrailing];
|
|
GSCSVariable *maxX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMaxX];
|
|
GSCSConstraint *maxXTrailingRelationshipConstraint = [GSCSConstraint constraintWithLeftVariable: maxX operator: GSCSConstraintOperatorEqual rightVariable: trailingVariable];
|
|
[self addSupportingSolverConstraint: maxXTrailingRelationshipConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalLeftConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *minX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinX];
|
|
GSCSVariable *leftVariable = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeLeft];
|
|
GSCSConstraint *minXLeadingRelationshipConstraint = [GSCSConstraint constraintWithLeftVariable: minX operator: GSCSConstraintOperatorEqual rightVariable: leftVariable];
|
|
[self addSupportingSolverConstraint: minXLeadingRelationshipConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalRightConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *maxX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMaxX];
|
|
GSCSVariable *rightVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeRight];
|
|
GSCSConstraint *maxXRightRelationshipConstraint = [GSCSConstraint constraintWithLeftVariable: maxX operator: GSCSConstraintOperatorEqual rightVariable: rightVariable];
|
|
[self addSupportingSolverConstraint: maxXRightRelationshipConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalBottomConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *minY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinY];
|
|
GSCSVariable *bottomVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeBottom];
|
|
GSCSConstraint *minYBottomRelationshipConstraint = [GSCSConstraint constraintWithLeftVariable: minY operator: GSCSConstraintOperatorEqual rightVariable: bottomVariable];
|
|
[self addSupportingSolverConstraint: minYBottomRelationshipConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalTopConstraintForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *maxY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMaxY];
|
|
GSCSVariable *topVariable = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeTop];
|
|
GSCSConstraint *maxYTopRelationshipConstraint = [GSCSConstraint constraintWithLeftVariable: maxY operator: GSCSConstraintOperatorEqual rightVariable: topVariable];
|
|
[self addSupportingSolverConstraint: maxYTopRelationshipConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalCenterXConstraintsForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *centerXVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeCenterX];
|
|
GSCSVariable *width = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeWidth];
|
|
GSCSVariable *minX = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinX];
|
|
|
|
GSCSLinearExpression *exp =
|
|
[[GSCSLinearExpression alloc] initWithVariable: minX];
|
|
[exp addVariable: width coefficient: 0.5];
|
|
GSCSConstraint *centerXConstraint = [GSCSConstraint constraintWithLeftVariable: centerXVariable operator: GSCSConstraintOperatorEqual rightExpression: exp];
|
|
|
|
[self addSupportingSolverConstraint: centerXConstraint
|
|
forSolverConstraint: constraint];
|
|
|
|
RELEASE(exp);
|
|
}
|
|
|
|
- (void) addInternalCenterYConstraintsForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *centerYVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeCenterY];
|
|
GSCSVariable *height = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeHeight];
|
|
GSCSVariable *minY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinY];
|
|
|
|
GSCSLinearExpression *exp =
|
|
[[GSCSLinearExpression alloc] initWithVariable: minY];
|
|
[exp addVariable: height coefficient: 0.5];
|
|
GSCSConstraint *centerYConstraint = [GSCSConstraint constraintWithLeftVariable: centerYVariable operator: GSCSConstraintOperatorEqual rightExpression: exp];
|
|
RELEASE(exp);
|
|
|
|
[self addSupportingSolverConstraint: centerYConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalFirstBaselineConstraintsForView: (NSView *)view
|
|
constraint:
|
|
(GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *firstBaselineVariable =
|
|
[self variableForView: view
|
|
andAttribute: GSLayoutAttributeFirstBaseline];
|
|
GSCSVariable *maxY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMaxY];
|
|
GSCSVariable *firstBaselineOffsetVariable =
|
|
[self variableForView: view
|
|
andViewAttribute: GSLayoutViewAttributeFirstBaselineOffsetFromTop];
|
|
|
|
GSCSLinearExpression *exp =
|
|
[[GSCSLinearExpression alloc] initWithVariable: maxY];
|
|
[exp addVariable: firstBaselineOffsetVariable coefficient: -1];
|
|
GSCSConstraint *firstBaselineConstraint = [GSCSConstraint constraintWithLeftVariable: firstBaselineVariable operator: GSCSConstraintOperatorEqual rightExpression: exp];
|
|
RELEASE(exp);
|
|
|
|
[self addSupportingConstraintForLayoutViewAttribute:
|
|
GSLayoutViewAttributeFirstBaselineOffsetFromTop
|
|
view: view
|
|
constraint: constraint];
|
|
[self addSupportingSolverConstraint: firstBaselineConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalBaselineConstraintsForView: (NSView *)view
|
|
constraint: (GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *baselineVariable =
|
|
[self variableForView: view andAttribute: GSLayoutAttributeBaseline];
|
|
GSCSVariable *minY = [self variableForView: view
|
|
andAttribute: GSLayoutAttributeMinY];
|
|
GSCSVariable *baselineOffsetVariable =
|
|
[self variableForView: view
|
|
andViewAttribute: GSLayoutViewAttributeBaselineOffsetFromBottom];
|
|
|
|
[self addSupportingConstraintForLayoutViewAttribute:
|
|
GSLayoutViewAttributeBaselineOffsetFromBottom
|
|
view: view
|
|
constraint: constraint];
|
|
GSCSLinearExpression *exp =
|
|
[[GSCSLinearExpression alloc] initWithVariable: minY];
|
|
[exp addVariable: baselineOffsetVariable];
|
|
GSCSConstraint *baselineConstraint = [GSCSConstraint constraintWithLeftVariable: baselineVariable operator: GSCSConstraintOperatorEqual rightExpression: exp];
|
|
RELEASE(exp);
|
|
|
|
[self addSupportingSolverConstraint: baselineConstraint
|
|
forSolverConstraint: constraint];
|
|
}
|
|
|
|
- (void) addInternalSolverConstraint: (GSCSConstraint *)constraint
|
|
forView: (NSView *)view
|
|
{
|
|
[self addSolverConstraint: constraint];
|
|
|
|
NSArray *internalViewConstraints =
|
|
[_internalConstraintsByViewIndex objectForKey: view];
|
|
if (internalViewConstraints == nil)
|
|
{
|
|
[_internalConstraintsByViewIndex setObject: [NSMutableArray array]
|
|
forKey: view];
|
|
}
|
|
[[_internalConstraintsByViewIndex objectForKey: view]
|
|
addObject: constraint];
|
|
}
|
|
|
|
- (void) addIntrinsicContentSizeConstraintsToView: (NSView *)view
|
|
{
|
|
NSSize intrinsicContentSize = [view intrinsicContentSize];
|
|
if (intrinsicContentSize.width != NSViewNoIntrinsicMetric)
|
|
{
|
|
[self
|
|
addSupportingIntrinsicSizeConstraintsToView: view
|
|
orientation: NSLayoutConstraintOrientationHorizontal
|
|
intrinsicSizeAttribute: GSLayoutViewAttributeIntrinsicWidth
|
|
dimensionAttribute: GSLayoutAttributeWidth];
|
|
}
|
|
if (intrinsicContentSize.height != NSViewNoIntrinsicMetric)
|
|
{
|
|
[self
|
|
addSupportingIntrinsicSizeConstraintsToView: view
|
|
orientation: NSLayoutConstraintOrientationVertical
|
|
intrinsicSizeAttribute: GSLayoutViewAttributeIntrinsicHeight
|
|
dimensionAttribute: GSLayoutAttributeHeight];
|
|
}
|
|
}
|
|
|
|
- (void) addSupportingIntrinsicSizeConstraintsToView: (NSView *)view
|
|
orientation: (NSLayoutConstraintOrientation)orientation
|
|
intrinsicSizeAttribute: (GSLayoutViewAttribute)intrinsicSizeAttribute
|
|
dimensionAttribute: (GSLayoutAttribute)dimensionAttribute
|
|
{
|
|
GSCSVariable *intrinsicContentDimension =
|
|
[self variableForView: view andViewAttribute: intrinsicSizeAttribute];
|
|
GSCSVariable *dimensionVariable =
|
|
[self variableForView: view andAttribute: dimensionAttribute];
|
|
|
|
GSCSVariable *intrinsicSizeVariable =
|
|
[self getExistingVariableForView: view
|
|
withViewAttribute: intrinsicSizeAttribute];
|
|
GSCSConstraint *intrinsicSizeConstraint =
|
|
[GSCSConstraint editConstraintWithVariable: intrinsicSizeVariable];
|
|
[self addInternalSolverConstraint: intrinsicSizeConstraint forView: view];
|
|
[self resolveVariableForView: view attribute: intrinsicSizeAttribute];
|
|
|
|
double huggingPriority =
|
|
[view contentHuggingPriorityForOrientation: orientation];
|
|
GSCSConstraint *huggingConstraint = [GSCSConstraint constraintWithLeftVariable: dimensionVariable operator: GSCSConstraintOperatorLessThanOrEqual rightVariable: intrinsicContentDimension];
|
|
GSCSStrength *huggingConstraintStrength =
|
|
[[GSCSStrength alloc] initWithName: nil strength: huggingPriority];
|
|
[huggingConstraint setStrength: huggingConstraintStrength];
|
|
RELEASE(huggingConstraintStrength);
|
|
|
|
[self addInternalSolverConstraint: huggingConstraint forView: view];
|
|
|
|
double compressionPriority =
|
|
[view contentCompressionResistancePriorityForOrientation: orientation];
|
|
GSCSConstraint *compressionConstraint = [GSCSConstraint constraintWithLeftVariable: dimensionVariable operator: GSCSConstraintOperationGreaterThanOrEqual rightVariable: intrinsicContentDimension];
|
|
GSCSStrength *compressionConstraintStrength =
|
|
[[GSCSStrength alloc] initWithName: nil strength: compressionPriority];
|
|
[compressionConstraint setStrength: compressionConstraintStrength];
|
|
RELEASE(compressionConstraintStrength);
|
|
|
|
[self addInternalSolverConstraint: compressionConstraint forView: view];
|
|
}
|
|
|
|
- (void) addSupportingConstraintForLayoutViewAttribute:
|
|
(GSLayoutViewAttribute)attribute
|
|
view: (NSView *)view
|
|
constraint:
|
|
(GSCSConstraint *)constraint
|
|
{
|
|
GSCSVariable *variable = [self getExistingVariableForView: view
|
|
withViewAttribute: attribute];
|
|
GSCSConstraint *editConstraint =
|
|
[GSCSConstraint editConstraintWithVariable: variable];
|
|
[self addSupportingSolverConstraint: editConstraint
|
|
forSolverConstraint: constraint];
|
|
[self resolveVariableForView: view attribute: attribute];
|
|
}
|
|
|
|
- (void) addObserverToConstraint: (NSLayoutConstraint *)constranit
|
|
{
|
|
[constranit addObserver: self
|
|
forKeyPath: @"constant"
|
|
options: NSKeyValueObservingOptionNew
|
|
context: nil];
|
|
}
|
|
|
|
- (void) observeValueForKeyPath: (NSString *)keyPath
|
|
ofObject: (id)object
|
|
change: (NSDictionary *)change
|
|
context: (void *)context
|
|
{
|
|
if ([object isKindOfClass: [NSLayoutConstraint class]])
|
|
{
|
|
NSLayoutConstraint *constraint = (NSLayoutConstraint *)object;
|
|
[self updateConstraint: constraint];
|
|
}
|
|
}
|
|
|
|
- (void) updateConstraint: (NSLayoutConstraint *)constraint
|
|
{
|
|
GSCSConstraint *kConstraint =
|
|
[self getExistingConstraintForAutolayoutConstraint: constraint];
|
|
[self removeSolverConstraint: kConstraint];
|
|
|
|
GSCSConstraint *newKConstraint =
|
|
[self solverConstraintForConstraint: constraint];
|
|
[self mapLayoutConstraint: constraint toSolverConstraint: newKConstraint];
|
|
[self addSolverConstraint: newKConstraint];
|
|
|
|
[self updateAlignmentRectsForTrackedViews];
|
|
}
|
|
|
|
- (void) removeInternalConstraintsForView: (NSView *)view
|
|
{
|
|
NSArray *internalViewConstraints =
|
|
[_internalConstraintsByViewIndex objectForKey: view];
|
|
FOR_IN(GSCSConstraint *, constraint, internalViewConstraints)
|
|
[self removeSolverConstraint: constraint];
|
|
END_FOR_IN(internalViewConstraints);
|
|
|
|
[_internalConstraintsByViewIndex setObject: nil forKey: view];
|
|
}
|
|
|
|
- (BOOL) hasConstraintsForView: (NSView *)view
|
|
{
|
|
NSNumber *viewIndex = [self indexForView: view];
|
|
return [[_constraintsByViewIndex objectForKey: viewIndex] count] > 0;
|
|
}
|
|
|
|
- (GSCSConstraint *) getExistingConstraintForAutolayoutConstraint:
|
|
(NSLayoutConstraint *)constraint
|
|
{
|
|
return [_constraintsByAutoLayoutConstaintHash objectForKey: constraint];
|
|
}
|
|
|
|
- (void) addConstraintAgainstViewConstraintsArray:
|
|
(NSLayoutConstraint *)constraint
|
|
{
|
|
NSNumber *firstItemViewIndex = [self indexForView: [constraint firstItem]];
|
|
NSMutableArray *constraintsForView =
|
|
[_constraintsByViewIndex objectForKey: firstItemViewIndex];
|
|
if (!constraintsForView)
|
|
{
|
|
constraintsForView = [NSMutableArray array];
|
|
[_constraintsByViewIndex setObject: constraintsForView
|
|
forKey: firstItemViewIndex];
|
|
}
|
|
[constraintsForView addObject: constraint];
|
|
|
|
if ([constraint secondItem] != nil)
|
|
{
|
|
NSNumber *secondItemViewIndex =
|
|
[self indexForView: [constraint secondItem]];
|
|
if ([_constraintsByViewIndex objectForKey: secondItemViewIndex])
|
|
{
|
|
[_constraintsByViewIndex setObject: [NSMutableArray array]
|
|
forKey: secondItemViewIndex];
|
|
}
|
|
[[_constraintsByViewIndex objectForKey: secondItemViewIndex]
|
|
addObject: constraint];
|
|
}
|
|
}
|
|
|
|
- (void) removeConstraintAgainstViewConstraintsArray:
|
|
(NSLayoutConstraint *)constraint
|
|
{
|
|
NSNumber *firstItemViewIndex = [self indexForView: [constraint firstItem]];
|
|
NSMutableArray *constraintsForFirstItem =
|
|
[_constraintsByViewIndex objectForKey: firstItemViewIndex];
|
|
|
|
NSUInteger indexOfConstraintInFirstItem =
|
|
[constraintsForFirstItem indexOfObject: constraint];
|
|
[constraintsForFirstItem removeObjectAtIndex: indexOfConstraintInFirstItem];
|
|
|
|
if ([constraint secondItem] != nil)
|
|
{
|
|
NSNumber *secondItemViewIndexIndex =
|
|
[self indexForView: [constraint secondItem]];
|
|
NSMutableArray *constraintsForSecondItem =
|
|
[_constraintsByViewIndex objectForKey: secondItemViewIndexIndex];
|
|
|
|
NSUInteger indexOfConstraintInSecondItem =
|
|
[constraintsForSecondItem indexOfObject: constraint];
|
|
[constraintsForSecondItem
|
|
removeObjectAtIndex: indexOfConstraintInSecondItem];
|
|
}
|
|
}
|
|
|
|
- (void) updateAlignmentRectsForTrackedViews
|
|
{
|
|
GSCSSolution *solution = [_solver solve];
|
|
[self updateAlignmentRectsForTrackedViewsForSolution: solution];
|
|
}
|
|
|
|
- (void) updateAlignmentRectsForTrackedViewsForSolution:
|
|
(GSCSSolution *)solution
|
|
{
|
|
NSMutableArray *viewsWithChanges = [NSMutableArray array];
|
|
FOR_IN(NSView *, view, _trackedViews)
|
|
BOOL viewAlignmentRectUpdated = [self updateViewAligmentRect: solution
|
|
view: view];
|
|
if (viewAlignmentRectUpdated)
|
|
{
|
|
[viewsWithChanges addObject: view];
|
|
}
|
|
END_FOR_IN(_trackedViews);
|
|
|
|
[self notifyViewsOfAlignmentRectChange: viewsWithChanges];
|
|
}
|
|
|
|
- (BOOL) updateViewAligmentRect: (GSCSSolution *)solution view: (NSView *)view
|
|
{
|
|
NSNumber *viewIndex = [self indexForView: view];
|
|
if ([self solverCanSolveAlignmentRectForView: view solution: solution])
|
|
{
|
|
NSRect existingAlignmentRect =
|
|
[self currentAlignmentRectForViewAtIndex: viewIndex];
|
|
BOOL isExistingAlignmentRect = NSIsEmptyRect(existingAlignmentRect);
|
|
NSRect solverAlignmentRect = [self solverAlignmentRectForView: view
|
|
solution: solution];
|
|
[self recordAlignmentRect: solverAlignmentRect forViewIndex: viewIndex];
|
|
|
|
if (isExistingAlignmentRect == NO
|
|
|| !NSEqualRects(solverAlignmentRect, existingAlignmentRect))
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL) solverCanSolveAlignmentRectForView: (NSView *)view
|
|
solution: (GSCSSolution *)solution
|
|
{
|
|
// FIXME
|
|
return NO;
|
|
}
|
|
|
|
- (void) recordAlignmentRect: (NSRect)alignmentRect
|
|
forViewIndex: (NSNumber *)viewIndex
|
|
{
|
|
NSValue *newRectValue = [NSValue valueWithRect: alignmentRect];
|
|
[_viewAlignmentRectByViewIndex setObject: newRectValue forKey: viewIndex];
|
|
}
|
|
|
|
- (NSRect) currentAlignmentRectForViewAtIndex: (NSNumber *)viewIndex
|
|
{
|
|
NSValue *existingRectValue =
|
|
[_viewAlignmentRectByViewIndex objectForKey: viewIndex];
|
|
if (existingRectValue == nil)
|
|
{
|
|
return NSZeroRect;
|
|
}
|
|
NSRect existingAlignmentRect;
|
|
[existingRectValue getValue: &existingAlignmentRect];
|
|
return existingAlignmentRect;
|
|
}
|
|
|
|
- (NSRect) solverAlignmentRectForView: (NSView *)view
|
|
solution: (GSCSSolution *)solution
|
|
{
|
|
// FIXME Get view solution from solver
|
|
return NSZeroRect;
|
|
}
|
|
|
|
- (void) notifyViewsOfAlignmentRectChange: (NSArray *)viewsWithChanges
|
|
{
|
|
FOR_IN(NSView *, view, viewsWithChanges)
|
|
[view _layoutEngineDidChangeAlignmentRect];
|
|
END_FOR_IN(viewsWithChanges);
|
|
}
|
|
|
|
- (NSRect) alignmentRectForView: (NSView *)view
|
|
{
|
|
// FIXME Get alignment rect for view from solver
|
|
return NSZeroRect;
|
|
}
|
|
|
|
- (void) addSupportingSolverConstraint: (GSCSConstraint *)supportingConstraint
|
|
forSolverConstraint: (GSCSConstraint *)constraint
|
|
{
|
|
[self addSolverConstraint: supportingConstraint];
|
|
|
|
if ([_supportingConstraintsByConstraint objectForKey: constraint] == nil)
|
|
{
|
|
[_supportingConstraintsByConstraint
|
|
setObject: [NSMutableArray array]
|
|
forKey: constraint];
|
|
}
|
|
[[_supportingConstraintsByConstraint objectForKey: constraint]
|
|
addObject: supportingConstraint];
|
|
}
|
|
|
|
- (void) addSolverConstraint: (GSCSConstraint *)constraint
|
|
{
|
|
[_solverConstraints addObject: constraint];
|
|
[_solver addConstraint: constraint];
|
|
}
|
|
|
|
- (void) removeSolverConstraint: (GSCSConstraint *)constraint
|
|
{
|
|
[_solver removeConstraint: constraint];
|
|
[_solverConstraints removeObject: constraint];
|
|
}
|
|
|
|
- (NSNumber *) indexForView: (NSView *)view
|
|
{
|
|
return [_viewIndexByViewHash
|
|
objectForKey: [NSNumber numberWithUnsignedInteger: [view hash]]];
|
|
}
|
|
|
|
- (NSArray *) constraintsForView: (NSView *)view
|
|
{
|
|
NSNumber *viewIndex = [self indexForView: view];
|
|
if (!viewIndex)
|
|
{
|
|
return [NSArray array];
|
|
}
|
|
|
|
NSMutableArray *constraintsForView = [NSMutableArray array];
|
|
NSArray *viewConstraints = [_constraintsByViewIndex objectForKey: viewIndex];
|
|
FOR_IN(NSLayoutConstraint *, constraint, viewConstraints)
|
|
if ([constraint firstItem] == view)
|
|
{
|
|
[constraintsForView addObject: constraint];
|
|
}
|
|
END_FOR_IN(viewConstraints);
|
|
|
|
return constraintsForView;
|
|
}
|
|
|
|
- (void) resolveVariableForView: (NSView *)view
|
|
attribute: (GSLayoutViewAttribute)attribute
|
|
{
|
|
GSCSVariable *editVariable = [self getExistingVariableForView: view
|
|
withViewAttribute: attribute];
|
|
CGFloat value = [self valueForView: view attribute: attribute];
|
|
|
|
[_solver suggestEditVariable: editVariable equals: value];
|
|
[self updateAlignmentRectsForTrackedViews];
|
|
}
|
|
|
|
- (CGFloat) valueForView: (NSView *)view
|
|
attribute: (GSLayoutViewAttribute)attribute
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case GSLayoutViewAttributeBaselineOffsetFromBottom:
|
|
return [view baselineOffsetFromBottom];
|
|
case GSLayoutViewAttributeFirstBaselineOffsetFromTop:
|
|
return [view firstBaselineOffsetFromTop];
|
|
case GSLayoutViewAttributeIntrinsicWidth:
|
|
return [view intrinsicContentSize].width;
|
|
case GSLayoutViewAttributeIntrinsicHeight:
|
|
return [view intrinsicContentSize].height;
|
|
default:
|
|
[[NSException exceptionWithName: @"Not handled"
|
|
reason: @"GSLayoutAttribute not handled"
|
|
userInfo: nil] raise];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
RELEASE(_trackedViews);
|
|
RELEASE(_viewAlignmentRectByViewIndex);
|
|
RELEASE(_viewIndexByViewHash);
|
|
RELEASE(_constraintsByViewIndex);
|
|
RELEASE(_layoutConstraintsBySolverConstraint);
|
|
RELEASE(_supportingConstraintsByConstraint);
|
|
RELEASE(_constraintsByAutoLayoutConstaintHash);
|
|
RELEASE(_internalConstraintsByViewIndex);
|
|
RELEASE(_solverConstraints);
|
|
RELEASE(_variablesByKey);
|
|
RELEASE(_solver);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|