diff --git a/src/Xamarin.iOSProviders/DesignTimeTypes.fs b/src/Xamarin.iOSProviders/DesignTimeTypes.fs deleted file mode 100644 index ffcc3c1..0000000 --- a/src/Xamarin.iOSProviders/DesignTimeTypes.fs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Xamarin.iOSProviders -open System -open System.Xml -open System.Xml.Linq -open System.Linq - -[] -module Xml = - let inline xn name = XName.op_Implicit(name) - -type IosAction = - {selector:string; destination:string; eventType:string; id:string} - static member Parse(action: XElement) = - {selector = action.Attribute(xn "selector").Value - destination = action.Attribute(xn "destination").Value - eventType = action.Attribute(xn "eventType").Value - id = action.Attribute(xn "id").Value } - -type Outlet = - {Name:string;Type:Type;Xml:string} - - static member Parse(outlet:XElement) = - let name = outlet.Attribute(xn "property").Value - let destination = outlet.Attribute(xn "destination").Value - let destinationElement = - let viewcontroller = outlet.Parent.Parent - let view = outlet.Parent.Parent.Descendants(xn "view") |> Seq.toArray - let subviews = view.Descendants(xn "subviews") |> Seq.toArray - subviews.Descendants() - |> Seq.tryFind (fun xx -> let id = xx.Attribute(xn "id") - if id = null then false - else id.Value = destination) - match destinationElement with - | Some element -> - match TypeSystem.typeMap.TryFind element.Name.LocalName with - | Some typ -> {Name=name;Type=typ;Xml= outlet.ToString()} - | None -> failwithf "Unknown Outlet type: %s" element.Name.LocalName - | None -> failwithf "Could not find outlet destination: %s" destination - -type ViewController = - {id:string; customClass:string; sceneMemberID:string} - static member Parse(vc: XElement) = - {id = vc.Attribute(xn "id").Value - customClass = vc.Attribute(xn "customClass").Value - sceneMemberID = vc.Attribute(xn "sceneMemberID").Value} diff --git a/src/Xamarin.iOSProviders/ParserIntegration.fsx b/src/Xamarin.iOSProviders/ParserIntegration.fsx new file mode 100644 index 0000000..456a303 --- /dev/null +++ b/src/Xamarin.iOSProviders/ParserIntegration.fsx @@ -0,0 +1,45 @@ +#load "ProvidedTypes.fs" +#load "TypeSystem.fs" +#I "bin/Debug" +#r "monotouch.dll" +#r "System.xml" +#r "System.Xml.Linq" +#r "FSharp.Compatibility.OCaml" +#r "MonoTouch.Design" + +open System +open System.IO +open System.Reflection +open System.Linq +open System.Xml.Linq +open System.Collections.Generic +open System.Reflection +open ProviderImplementation.ProvidedTypes +open Microsoft.FSharp.Core.CompilerServices +open Microsoft.FSharp.Quotations +open MonoTouch.Design +open MonoTouch.Foundation +open MonoTouch.UIKit +open ProviderImplementation.ProvidedTypes + +let storyboardName = "/Users/dave/code/xamarin/fsharp-iOS-designer/src/ReferenceApps/CS_ViewController/garbage.storyboard" +let stream = File.OpenRead (storyboardName) +let xdoc = XDocument.Load(stream) +let parsed = Parser.Instance.Parse(xdoc.Root) + +let storyboard = + match parsed with + | :? Storyboard as sb -> + sb + //| :? Xib as xib -> + //TODO + | _ -> failwith "broken" + +let sc = storyboard.Scenes.[0] +let vc = sc.ViewController +let outlet = vc.Outlets.[0] + +let iuthing = storyboard.FindById(outlet.Destination) :?> ProxiedUiKitObject +iuthing.DisplayClassName +outlet + diff --git a/src/Xamarin.iOSProviders/TypeSystem.fs b/src/Xamarin.iOSProviders/TypeSystem.fs deleted file mode 100644 index 6ad78f3..0000000 --- a/src/Xamarin.iOSProviders/TypeSystem.fs +++ /dev/null @@ -1,107 +0,0 @@ -namespace Xamarin.iOSProviders -open System -open System.Drawing -open MonoTouch -open MonoTouch.UIKit -open MonoTouch.Foundation -open MonoTouch.Design - -type StaticHelpers() = - static member InstantiateInitialViewController<'a when 'a :> NSObject>( storyboardName) = - let mainStoryboard = UIStoryboard.FromName (storyboardName, null) - let sb = mainStoryboard.InstantiateInitialViewController () - let theType = sb.GetType() - sb :?> 'a - - -module TypeSystem = - let typeMap = - - //TODO replace this with a call to xml to objc type and then look in the monotouch assembly for a type with a register attribute that matches the objc name - Map.ofList - [("activityIndicatorView", typeof) - ("adBannerView", typeof) - ("attributedString", typeof) - ("autoresizingMask", typeof) - ("barButtonItem", typeof) - ("button", typeof) - ("color", typeof) - ("collectionReusableView", typeof) - ("collectionView", typeof) - ("collectionViewCell", typeof) - ("collectionViewController", typeof) - ("collectionViewLayout", typeof) - ("collectionViewFlowLayout", typeof) - ("constraint", typeof) - ("containerView", typeof) - ("control", typeof) - ("customObject", typeof) - ("dataDetectorType", typeof) - ("date", typeof) - ("datePicker", typeof) - ("dependencies", typeof) - ("deployment", typeof) - ("development", typeof) - ("extendedEdge", typeof) - ("font", typeof) - ("fontDescription", typeof) - ("glkView", typeof) - ("glkViewController", typeof) - ("imageView", typeof) - ("inset", typeof) - ("integer", typeof) - ("label", typeof) - ("locale", typeof) - ("mapView", typeof) - ("mutableData", typeof) - ("mutableString", typeof) - ("navigationBar", typeof) - ("navigationController", typeof) - ("navigationItem", typeof) - ("offsetWrapper", typeof) - ("pageControl", typeof) - ("panGestureRecognizer", typeof) - ("paragraphStyle", typeof) - ("pickerView", typeof) - ("pinchGestureRecognizer", typeof) - ("plugIn", typeof) - ("pageViewController", typeof) - ("progressView", typeof) - ("point", typeof) - ("pongPressGestureRecognizer", typeof) // the class name is an Xcode typo that we emulate - ("real", typeof) - ("rect", typeof) - ("rotationGestureRecognizer", typeof) - ("scrollView", typeof) - ("searchBar", typeof) - ("searchDisplayController", typeof) - ("segmentedControl", typeof) - ("size", typeof) - ("slider", typeof) - ("splitViewController", typeof) - ("splitViewDetailSimulatedSizeMetrics", typeof) - ("splitViewMasterSimulatedSizeMetrics", typeof) - ("state", typeof) //not needed client side// - ("stepper", typeof) - ("string", typeof) - ("swipeGestureRecognizer", typeof) - ("switch", typeof) - ("tabBar", typeof) - ("tabBarController", typeof) - ("tabBarItem", typeof) - ("tableView", typeof) - ("tableViewCell", typeof) - ("tableViewController", typeof) - ("tableViewSection", typeof) - ("tapGestureRecognizer", typeof) - ("textAttributes", typeof) - ("textField", typeof) - ("toolbarItems", typeof) - ("textView", typeof) - ("timeZone", typeof) - ("toolbar", typeof) - ("view", typeof) - ("viewController", typeof) - ("viewControllerLayoutGuide", typeof) - ("window", typeof) - ("webView", typeof) ] diff --git a/src/Xamarin.iOSProviders/Xamarin.iOSProviders.fsproj b/src/Xamarin.iOSProviders/Xamarin.iOSProviders.fsproj index 60d87c7..16c83dc 100644 --- a/src/Xamarin.iOSProviders/Xamarin.iOSProviders.fsproj +++ b/src/Xamarin.iOSProviders/Xamarin.iOSProviders.fsproj @@ -77,14 +77,13 @@ \Applications\Xamarin Studio.app\Contents\MacOS\lib\monodevelop\AddIns\MonoDevelop.IPhone\MonoTouch.Design.Client.dll + - - @@ -94,5 +93,6 @@ + \ No newline at end of file diff --git a/src/Xamarin.iOSProviders/iOSDesignerProvider.fs b/src/Xamarin.iOSProviders/iOSDesignerProvider.fs index 7722d54..9346b82 100644 --- a/src/Xamarin.iOSProviders/iOSDesignerProvider.fs +++ b/src/Xamarin.iOSProviders/iOSDesignerProvider.fs @@ -14,6 +14,7 @@ open MonoTouch.Foundation open MonoTouch.UIKit open Microsoft.FSharp.Compatibility.OCaml open iOSDesignerTypeProvider.ProvidedTypes +open MonoTouch.Design module Sanitise = let cleanTrailing = String.trimEnd [|':'|] @@ -44,25 +45,31 @@ module Attributes = module TypeBuilder = - let buildTypes (designerFile:Uri) (viewControllerElement: XElement) isAbstract addUnitCtor register = - let actions = - viewControllerElement.Descendants(Xml.xn "action") - |> Seq.map IosAction.Parse - let outlets = - viewControllerElement.Descendants(Xml.xn "outlet") - |> Seq.map Outlet.Parse |> Seq.toArray + let typeMap (proxy:ProxiedUiKitObject) = + //TODO: Expand this to also search in user assemblies + let monotouchAssembly = typeof.Assembly + query { for typ in monotouchAssembly.ExportedTypes do + where (query {for ca in typ.CustomAttributes do + exists (ca.AttributeType = typeof && + match ca.ConstructorArguments |> Seq.map (fun ca -> ca.Value) |> Seq.toList with + | [:? string as name; :? bool as isWrapper] -> name = proxy.ClassName + | _ -> false)}) + select typ + exactlyOne } - let viewController = ViewController.Parse(viewControllerElement) - - // Generate the required type - let controllerType = - TypeSystem.typeMap - |> Map.filter (fun k v -> k.EndsWith("Controller")) - |> Map.tryFind viewController.sceneMemberID - |> function Some(t) -> t | _ -> failwith "No valid type found" + let buildTypes (designerFile:Uri) (vc: ProxiedViewController) isAbstract addUnitCtor register (config:TypeProviderConfig) = + let actions = + match vc.View with + | null -> Seq.empty + | view -> + match view.Subviews with + | null -> Seq.empty + | subviews -> subviews |> Seq.map (fun sv -> sv.Actions) |> Seq.concat - let providedController = ProvidedTypeDefinition(viewController.customClass + "Base", Some controllerType, IsErased=false ) + let controllerType = typeMap vc + + let providedController = ProvidedTypeDefinition(vc.CustomClass + "Base", Some controllerType, IsErased=false ) providedController.SetAttributes (if isAbstract then TypeAttributes.Public ||| TypeAttributes.Class ||| TypeAttributes.Abstract else TypeAttributes.Public ||| TypeAttributes.Class) @@ -83,27 +90,27 @@ module TypeBuilder = providedController.AddMember(emptyctor) if register then - let register = Attributes.MakeRegisterAttributeData viewController.customClass + let register = Attributes.MakeRegisterAttributeData vc.CustomClass providedController.AddCustomAttribute(register) - providedController.AddMember <| ProvidedLiteralField("CustomClass", typeof, viewController.customClass) + providedController.AddMember <| ProvidedLiteralField("CustomClass", typeof, vc.CustomClass) //actions mutable assignment style---------------------------- //TODO add option for ObservableSource, potentially unneeded as outlets exposes this with observable... for action in actions do //create a backing field fand property or the action - let actionField, actionProperty = ProvidedTypes.ProvidedPropertyWithField(Sanitise.makeFieldName action.selector, - Sanitise.makeMethodName action.selector, + let actionField, actionProperty = ProvidedTypes.ProvidedPropertyWithField(Sanitise.makeFieldName action.Selector, + Sanitise.makeMethodName action.Selector, typeof>) let actionBinding = - ProvidedMethod(methodName=Sanitise.makeSelectorMethodName action.selector, + ProvidedMethod(methodName=Sanitise.makeSelectorMethodName action.Selector, parameters=[ProvidedParameter("sender", typeof)], returnType=typeof, InvokeCode = fun args -> let instance = Expr.Cast>(Expr.FieldGet(args.[0], actionField)) <@@ if %instance <> null then (%instance).Invoke(%%args.[1]) @@>) - actionBinding.AddCustomAttribute(Attributes.MakeActionAttributeData(action.selector)) + actionBinding.AddCustomAttribute(Attributes.MakeActionAttributeData(action.Selector)) actionBinding.SetMethodAttrs MethodAttributes.Private providedController.AddMember actionField @@ -120,11 +127,13 @@ module TypeBuilder = //outlets----------------------------------------- let providedOutlets = - outlets + vc.Outlets |> Array.map (fun outlet -> - let outletField, outletProperty = ProvidedTypes.ProvidedPropertyWithField(Sanitise.makeFieldName outlet.Name, - Sanitise.makePropertyName outlet.Name, - outlet.Type) + //type lookup here + let uiProxy = vc.Storyboard.FindById(outlet.Destination) :?> ProxiedUiKitObject + let outletField, outletProperty = ProvidedTypes.ProvidedPropertyWithField(Sanitise.makeFieldName outlet.Property, + Sanitise.makePropertyName outlet.Property, + typeMap uiProxy) outletProperty.AddCustomAttribute <| Attributes.MakeOutletAttributeData() ///takes an instance returns a disposal expresion @@ -215,12 +224,17 @@ type iOSDesignerProvider(config: TypeProviderConfig) as this = //generate storyboard container let container = ProvidedTypeDefinition(asm, ns, typeName, Some(typeof), IsErased=false) - //TODO try to use MonoTouch.Design parsing, extract the models action/outlets etc - //let parsed = MonoTouch.Design.ClientParser.Instance.Parse(xdoc.Root) - let viewControllerElements = xdoc.Descendants(Xml.xn "viewController") + let scenes = + match Parser.Instance.Parse(xdoc.Root) with + | :? Storyboard as sb -> + sb.Scenes + //TODO: Support Xibs! + | :? Xib as xib -> failwith "Xib files are currently not supported" + | _ -> failwith "Could not parse file, no storyboard or xib types were found" + let types = - viewControllerElements - |> Seq.map (fun vc -> TypeBuilder.buildTypes designerFile vc isAbstract addUnitCtor register) + scenes + |> Seq.map (fun controller -> TypeBuilder.buildTypes designerFile controller.ViewController isAbstract addUnitCtor register config) //Add the types to the container for pt in types do