-
-
Save garuma/3de3bbeb954ad5679e87 to your computer and use it in GitHub Desktop.
A set of helper extension methods to work with MonoMac/Cocoa autolayout constraints.
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
using System; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using MonoMac.AppKit; | |
namespace ConstraintsEx | |
{ | |
public struct ConstraintProxy { | |
public int Left, Top, Right, Bottom, Leading, Trailing, CenterX, CenterY, Baseline, Height, Width; | |
} | |
public static class ConstraintExtensions | |
{ | |
public static NSLayoutConstraint[] ConstraintFill (this NSView child, NSView container, int padding = 0) | |
{ | |
return new NSLayoutConstraint[] { | |
NSLayoutConstraint.Create (child, NSLayoutAttribute.Left, NSLayoutRelation.Equal, container, NSLayoutAttribute.Left, 1, padding), | |
NSLayoutConstraint.Create (child, NSLayoutAttribute.Right, NSLayoutRelation.Equal, container, NSLayoutAttribute.Right, 1, -padding), | |
NSLayoutConstraint.Create (child, NSLayoutAttribute.Top, NSLayoutRelation.Equal, container, NSLayoutAttribute.Top, 1, container.IsFlipped ? padding : -padding), | |
NSLayoutConstraint.Create (child, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, container, NSLayoutAttribute.Bottom, 1, container.IsFlipped ? -padding : padding), | |
}; | |
} | |
public static NSLayoutConstraint ConstraintTo (this NSView view1, NSView view2, Expression<Func<ConstraintProxy, ConstraintProxy, bool>> expression) | |
{ | |
var mainExpression = expression.Body as BinaryExpression; | |
NSLayoutRelation relation = NSLayoutRelation.Equal; | |
switch (mainExpression.NodeType) { | |
case ExpressionType.Equal: | |
relation = NSLayoutRelation.Equal; | |
break; | |
case ExpressionType.LessThanOrEqual: | |
relation = NSLayoutRelation.LessThanOrEqual; | |
break; | |
case ExpressionType.GreaterThanOrEqual: | |
relation = NSLayoutRelation.GreaterThanOrEqual; | |
break; | |
default: | |
throw new ArgumentException ("Relation " + mainExpression.NodeType.ToString () + " not valid"); | |
} | |
var propLeft = (NSLayoutAttribute)Enum.Parse (typeof (NSLayoutAttribute), ((MemberExpression)mainExpression.Left).Member.Name); | |
var propRight = propLeft; | |
if (mainExpression.Right.NodeType == ExpressionType.Constant) { | |
var c = Convert.ToSingle (((ConstantExpression)mainExpression.Right).Value); | |
return NSLayoutConstraint.Create (view1, propLeft, relation, null, NSLayoutAttribute.NoAttribute, 1, c); | |
} else if (mainExpression.Right is MemberExpression) { | |
propRight = (NSLayoutAttribute)Enum.Parse (typeof (NSLayoutAttribute), ((MemberExpression)mainExpression.Right).Member.Name); | |
return NSLayoutConstraint.Create (view1, propLeft, relation, view2, propRight, 1, 0); | |
} | |
var addNode = mainExpression.Right as BinaryExpression; | |
var mulNode = addNode; | |
var constant = 0f; | |
var multiplier = 1f; | |
if (addNode.Left.NodeType == ExpressionType.Constant) { | |
mulNode = addNode.Right as BinaryExpression; | |
constant = Convert.ToSingle (((ConstantExpression)addNode.Left).Value); | |
} else { | |
mulNode = addNode.Left as BinaryExpression; | |
constant = Convert.ToSingle (((ConstantExpression)addNode.Right).Value); | |
} | |
constant *= addNode.NodeType == ExpressionType.Subtract ? -1 : 1; | |
if (mulNode != null) { | |
if (mulNode.Left.NodeType == ExpressionType.Constant) { | |
multiplier = Convert.ToSingle (((ConstantExpression)mulNode.Left).Value); | |
propRight = (NSLayoutAttribute)Enum.Parse (typeof (NSLayoutAttribute), ((MemberExpression)mulNode.Right).Member.Name); | |
} else { | |
multiplier = Convert.ToSingle (((ConstantExpression)mulNode.Right).Value); | |
propRight = (NSLayoutAttribute)Enum.Parse (typeof (NSLayoutAttribute), ((MemberExpression)mulNode.Left).Member.Name); | |
} | |
if (mulNode.NodeType == ExpressionType.Divide) | |
multiplier = 1.0f / multiplier; | |
} else { | |
var member = (MemberExpression)(addNode.Right is MemberExpression ? addNode.Right : addNode.Left); | |
propRight = (NSLayoutAttribute)Enum.Parse (typeof (NSLayoutAttribute), member.Member.Name); | |
} | |
//Console.WriteLine ("v1.{0} {1} v2.{2} * {3} + {4}", propLeft, relation, propRight, multiplier, constant); | |
return NSLayoutConstraint.Create (view1, propLeft, relation, view2, propRight, multiplier, constant); | |
} | |
public static void DoConstraints (this NSView view, params NSLayoutConstraint[] constraints) | |
{ | |
view.AddConstraints (constraints); | |
} | |
public static NSLayoutConstraint SticksTo (this NSView child, NSView container, NSLayoutAttribute attribute) | |
{ | |
return NSLayoutConstraint.Create (child, attribute, NSLayoutRelation.Equal, container, attribute, 1, 0); | |
} | |
public static NSLayoutConstraint[] SticksTo (this NSView child, NSView container, params NSLayoutAttribute[] attributes) | |
{ | |
return attributes | |
.Select (a => NSLayoutConstraint.Create (child, a, NSLayoutRelation.Equal, container, a, 1, 0)) | |
.ToArray (); | |
} | |
} | |
} |
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
using System; | |
using NUnit.Framework; | |
using System.Linq; | |
using System.Xml; | |
using System.Xml.Linq; | |
using System.Net; | |
using MonoMac.AppKit; | |
namespace ConstraintsEx | |
{ | |
[TestFixture] | |
public class ConstraintExtensionsTests | |
{ | |
NSView view1; | |
NSView view2; | |
[SetUp] | |
public void Setup () | |
{ | |
view1 = new NSView (); | |
view2 = new NSView (); | |
} | |
[Test] | |
public void TestWithExpressions () | |
{ | |
AssertSameConstraint (NSLayoutConstraint.Create (view1, NSLayoutAttribute.Height, NSLayoutRelation.GreaterThanOrEqual, null, NSLayoutAttribute.NoAttribute, 1, 60), | |
view1.ConstraintTo (null, (v1, v2) => v1.Height >= 60)); | |
AssertSameConstraint (NSLayoutConstraint.Create (view1, NSLayoutAttribute.Top, NSLayoutRelation.Equal, view2, NSLayoutAttribute.Top, 1, 21), | |
view1.ConstraintTo (view2, (v1, v2) => v1.Top == v2.Top + 21)); | |
AssertSameConstraint (NSLayoutConstraint.Create (view1, NSLayoutAttribute.Height, NSLayoutRelation.LessThanOrEqual, view2, NSLayoutAttribute.Height, 1, 0), | |
view1.ConstraintTo (view2, (v1, v2) => v1.Height <= v2.Height)); | |
AssertSameConstraint (NSLayoutConstraint.Create (view1, NSLayoutAttribute.Left, NSLayoutRelation.Equal, view2, NSLayoutAttribute.CenterX, 1, 3), | |
view1.ConstraintTo (view2, (v1, v2) => v1.Left == v2.CenterX + 3)); | |
} | |
[Test] | |
public void SticksToMultipleConstraintsTest () | |
{ | |
var expected = new NSLayoutConstraint[] { | |
NSLayoutConstraint.Create (view1, NSLayoutAttribute.Top, NSLayoutRelation.Equal, view2, NSLayoutAttribute.Top, 1, 0), | |
NSLayoutConstraint.Create (view1, NSLayoutAttribute.Left, NSLayoutRelation.Equal, view2, NSLayoutAttribute.Left, 1, 0), | |
NSLayoutConstraint.Create (view1, NSLayoutAttribute.Width, NSLayoutRelation.Equal, view2, NSLayoutAttribute.Width, 1, 0) | |
}; | |
var actual = view1.SticksTo (view2, NSLayoutAttribute.Top, NSLayoutAttribute.Left, NSLayoutAttribute.Width); | |
Assert.AreEqual (expected.Length, actual.Length); | |
for (int i = 0; i < expected.Length; i++) | |
AssertSameConstraint (expected [i], actual [i]); | |
} | |
void AssertSameConstraint (NSLayoutConstraint c1, NSLayoutConstraint c2) | |
{ | |
Assert.AreEqual (c1.FirstItem, c2.FirstItem, "FirstItem"); | |
Assert.AreEqual (c1.SecondItem, c2.SecondItem, "SecondItem"); | |
Assert.AreEqual (c1.Relation, c2.Relation, "Relation"); | |
Assert.AreEqual (c1.FirstAttribute, c2.FirstAttribute, "FirstAttribute"); | |
Assert.AreEqual (c1.SecondAttribute, c2.SecondAttribute, "SecondAttribute"); | |
Assert.AreEqual (c1.Constant, c2.Constant, "Constant"); | |
Assert.AreEqual (c1.Multiplier, c2.Multiplier, "Multiplier"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment