Skip to content

Instantly share code, notes, and snippets.

@7sharp9
Forked from praeclarum/EasyLayout.fs
Last active Aug 29, 2015
Embed
What would you like to do?
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 &amp;&amp; operator.</para>
/// <code><@ button.Frame.Left &gt;= text.Frame.Right + 22 &amp;&amp;
/// 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