-
-
Save 7sharp9/d807ee3178856f0e0f09 to your computer and use it in GitHub Desktop.
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
module EasyLayout | |
open System | |
open System.Drawing | |
open Microsoft.FSharp.Quotations | |
open Microsoft.FSharp.Quotations.Patterns | |
open Microsoft.FSharp.Quotations.DerivedPatterns | |
open MonoTouch.Foundation | |
open MonoTouch.UIKit | |
module private Utilities = | |
let rec eval e = | |
match e with | |
| FieldGet (Some o, f) -> f.GetValue (eval o) | |
| FieldGet (None, f) -> f.GetValue (null) | |
| PropertyGet (None, p, i) -> p.GetValue (null, i |> Seq.map eval |> Seq.toArray) | |
| PropertyGet (Some o, p, i) -> p.GetValue (eval o, i |> Seq.map eval |> Seq.toArray) | |
| Value (x, _) -> x | |
| _ -> failwithf "Don't know how to eval %A" e | |
let toAttr m = | |
match m with | |
| "X" | "Left" -> NSLayoutAttribute.Left | |
| "Y" | "Top" -> NSLayoutAttribute.Top | |
| "Width" -> NSLayoutAttribute.Width | |
| "Height" -> NSLayoutAttribute.Height | |
| "Bottom" -> NSLayoutAttribute.Bottom | |
| "Right" -> NSLayoutAttribute.Right | |
| "CenterX" | "RectangleF.get_CenterX" -> NSLayoutAttribute.CenterX | |
| "CenterY" | "RectangleF.get_CenterY" -> NSLayoutAttribute.CenterY | |
| "Baseline" | "RectangleF.get_Baseline" -> NSLayoutAttribute.Baseline | |
| _ -> NSLayoutAttribute.NoAttribute | |
let (|ConstrainableProp|_|) (m:Reflection.MemberInfo) = | |
match toAttr m.Name with NSLayoutAttribute.NoAttribute -> None | others -> Some(others) | |
let (|FrameProp|_|) (pi:Reflection.PropertyInfo) = if pi.Name = "Frame" then Some(pi) else None | |
let (|GetFrameProp|_|) e = | |
match e with | |
| Let (_, PropertyGet (Some o, FrameProp(fn), _), PropertyGet (_, ConstrainableProp(pn), _)) -> Some (eval o :?> NSObject, pn) | |
| Call (_, ConstrainableProp(pn), [PropertyGet (Some o, FrameProp(fn), _)]) -> Some (eval o :?> NSObject, pn) | |
| _ -> None | |
let compileLeftSide = function | |
| GetFrameProp (x) -> x | |
| side -> failwithf "Left hand side of constraint is expected to be a UIView.Frame property. It was: %A" side | |
let (|NamedMethod|_|) name (m:Reflection.MethodInfo) = if m.Name = name then Some(m) else None | |
let (|Mul|_|) = function | |
| GetFrameProp (x, p) -> Some (x, p, 1.0f) | |
| Call (_, NamedMethod "op_Multiply" _, [l; GetFrameProp (x, p)]) -> Some (x, p, Convert.ToSingle (eval l)) | |
| Call (_, NamedMethod "op_Multiply" _, [GetFrameProp (x, p); l]) -> Some (x, p, Convert.ToSingle (eval l)) | |
| _ -> None | |
let compileRightSide side = | |
match side with | |
| Mul x -> (Some x, 0.0f) | |
| Call (_, NamedMethod "op_Addition" _, [Mul(x); c]) -> (Some x, Convert.ToSingle (eval c)) | |
| Call (_, NamedMethod "op_Subtraction" _, [Mul(x); c]) -> (Some x, -Convert.ToSingle (eval c)) | |
| Value (x, _) -> (None, Convert.ToSingle (x)) | |
| FieldGet _ -> (None, Convert.ToSingle (eval side)) | |
| _ -> failwithf "Unrecognized right hand side: %A." side | |
let compileConstraint left right rel = | |
let (firstObj, firstAttr) = compileLeftSide left | |
let (maybeObj, add) = compileRightSide right | |
match maybeObj with | |
| None -> NSLayoutConstraint.Create (firstObj, firstAttr, rel, null, NSLayoutAttribute.NoAttribute, 0.0f, add) | |
| Some (secObj, secAttr, mul) -> NSLayoutConstraint.Create (firstObj, firstAttr, rel, secObj, secAttr, mul, add) | |
let (|ToRel|_|) (m:System.Reflection.MethodInfo) = | |
match m.Name with | |
| "op_Equality" -> Some NSLayoutRelation.Equal | |
| "op_LessThanOrEqual" -> Some NSLayoutRelation.LessThanOrEqual | |
| "op_GreaterThanOrEqual" -> Some NSLayoutRelation.GreaterThanOrEqual | |
| _ -> None | |
let rec compileConstraints expr = | |
match expr with | |
| NewArray (_, es) -> es |> Seq.collect compileConstraints |> Seq.toList | |
| IfThenElse (i, t, e) -> compileConstraints i @ compileConstraints t @ compileConstraints e | |
| Call (_, ToRel(m), [l; r]) -> [compileConstraint l r m] | |
| Value _ -> [] | |
| _ -> failwithf "Unable to recognize constraints in expression: %A" expr | |
type RectangleF with | |
member this.CenterX = this.X + this.Width / 2.0f | |
member this.CenterY = this.Y + this.Height / 2.0f | |
member this.Baseline = 0.0f | |
type UIView with | |
/// <summary> | |
/// <para>Constrains the layout of subviews according to equations and | |
/// inequalities specified in <paramref name="constraints"/>. Issue | |
/// multiple constraints per call using the && operator.</para> | |
/// <code><@ button.Frame.Left >= text.Frame.Right + 22 && | |
/// button.Frame.Width = View.Frame.Width * 0.42f @></code> | |
/// </summary> | |
/// <param name="constraints">Constraint equations and inequalities.</param> | |
member this.ConstrainLayout (constraints) = | |
let cs = Utilities.compileConstraints constraints |> Seq.toArray | |
this.AddConstraints (cs) | |
for x in cs do | |
(x.FirstItem :?> UIView).TranslatesAutoresizingMaskIntoConstraints <- false | |
cs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment