From 2cde3cf377707ed92c8030c995fdcd77a172649a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9=20Larivi=C3=A8re?= Date: Mon, 10 Jun 2019 20:40:48 +0200 Subject: [PATCH] Add missing properties for Shell related controls --- src/Fabulous.Core/ViewConverters.fs | 135 ++++++++++++++++++++++-- tools/Generator/CodeGenerator.fs | 5 +- tools/Generator/Resolvers.fs | 5 +- tools/Generator/Xamarin.Forms.Core.json | 125 +++++++++++++++++++--- 4 files changed, 247 insertions(+), 23 deletions(-) diff --git a/src/Fabulous.Core/ViewConverters.fs b/src/Fabulous.Core/ViewConverters.fs index b626f8fa6..30d6e3121 100644 --- a/src/Fabulous.Core/ViewConverters.fs +++ b/src/Fabulous.Core/ViewConverters.fs @@ -131,7 +131,7 @@ type ViewElementCell() = modelOpt <- None | None -> () -type ItemViewElementCell() = +type ContentViewElement() = inherit ContentView() let mutable listElementOpt : IItemListElement option = None @@ -179,10 +179,10 @@ type CustomListView() = inherit ListView(ItemTemplate=DataTemplate(typeof)) type CustomCollectionListView() = - inherit CollectionView(ItemTemplate=DataTemplate(typeof)) + inherit CollectionView(ItemTemplate=DataTemplate(typeof)) type CustomCarouselView() = - inherit CarouselView(ItemTemplate=DataTemplate(typeof)) + inherit CarouselView(ItemTemplate=DataTemplate(typeof)) /// A custom control for the ListViewGrouped view element type CustomGroupListView() = @@ -200,6 +200,18 @@ type CustomContentPage() as self = base.OnSizeAllocated(width, height) sizeAllocated.Trigger(width, height) +/// A custom SearchHandler which exposes the overridable methods OnQueryChanged, OnQueryConfirmed and OnItemSelected as events +type CustomSearchHandler() = + inherit SearchHandler(ItemTemplate=DataTemplate(typeof)) + + member val QueryChanged = ignore with get, set + member val QueryConfirmed = ignore with get, set + member val ItemSelected: obj -> unit = ignore with get, set + + override this.OnQueryChanged(oldValue, newValue) = this.QueryChanged (oldValue, newValue) + override this.OnQueryConfirmed() = this.QueryConfirmed () + override this.OnItemSelected(item) = this.ItemSelected item + [] module Converters = open System.Collections.ObjectModel @@ -452,6 +464,12 @@ module Converters = oc updateCollectionGeneric (ValueOption.map seqToArray prevCollOpt) (ValueOption.map seqToArray collOpt) targetColl ListElementData (fun _ _ _ -> ()) canReuseChild (fun _ curr target -> target.Key <- curr) + /// Update the items in a SearchHandler control, given previous and current view elements + let updateSearchHandlerItems (prevCollOpt: seq<'T> voption) (collOpt: seq<'T> voption) (target: Xamarin.Forms.SearchHandler) = + let targetColl = List() + updateCollectionGeneric (ValueOption.map seqToArray prevCollOpt) (ValueOption.map seqToArray collOpt) targetColl ItemListElementData (fun _ _ _ -> ()) canReuseChild (fun _ curr target -> target.Key <- curr) + target.ItemsSource <- targetColl + /// Update the items in a CollectionView control, given previous and current view elements let internal updateCollectionViewItems (prevCollOpt: seq<'T> voption) (collOpt: seq<'T> voption) (target: Xamarin.Forms.CollectionView) = let targetColl = @@ -771,7 +789,17 @@ module Converters = | :? ShellItem as shellItem -> shellItem | child -> failwithf "%s is not compatible with the type ShellItem" (child.GetType().Name) - updateCollectionGeneric prevCollOpt collOpt target.Items create (fun _ _ _ -> ()) (fun _ _ -> true) updateChild + let update prevViewElement (currViewElement: ViewElement) (target: ShellItem) = + let realTarget = + match currViewElement.TargetType with + | t when t = typeof -> target.Items.[0].Items.[0] :> Element + | t when t = typeof -> target.Items.[0].Items.[0] :> Element + | t when t = typeof -> target.Items.[0] :> Element + | t when t = typeof -> target.GetType().GetProperty("MenuItem").GetValue(target) :?> Element // MenuShellItem is marked as internal + | _ -> target :> Element + updateChild prevViewElement currViewElement realTarget + + updateCollectionGeneric prevCollOpt collOpt target.Items create (fun _ _ _ -> ()) (fun _ _ -> true) update /// Update the menu items of a ShellContent, given previous and current view elements let internal updateMenuItemsShellContent (prevCollOpt: ViewElement array voption) (collOpt: ViewElement array voption) (target: Xamarin.Forms.ShellContent) = @@ -789,7 +817,15 @@ module Converters = | :? ShellSection as shellSection -> shellSection | child -> failwithf "%s is not compatible with the type ShellSection" (child.GetType().Name) - updateCollectionGeneric prevCollOpt collOpt target.Items create (fun _ _ _ -> ()) (fun _ _ -> true) updateChild + let update prevViewElement (currViewElement: ViewElement) (target: ShellSection) = + let realTarget = + match currViewElement.TargetType with + | t when t = typeof -> target.Items.[0] :> BaseShellItem + | t when t = typeof -> target.Items.[0] :> BaseShellItem + | _ -> target :> BaseShellItem + updateChild prevViewElement currViewElement realTarget + + updateCollectionGeneric prevCollOpt collOpt target.Items create (fun _ _ _ -> ()) (fun _ _ -> true) update /// Update the items of a ShellSection, given previous and current view elements let internal updateShellSectionItems (prevCollOpt: ViewElement array voption) (collOpt: ViewElement array voption) (target: Xamarin.Forms.ShellSection) = @@ -815,7 +851,7 @@ module Converters = | _, ValueSome newVal -> target.SetValue(Xamarin.Forms.SearchHandler.SelectedItemProperty, newVal) /// Update the IsCheckedProperty of a BaseShellItem, given previous and current IsChecked - let internal updateIsCecked prevValue currValue (target: Xamarin.Forms.BaseShellItem) = + let internal updateIsChecked prevValue currValue (target: Xamarin.Forms.BaseShellItem) = match prevValue, currValue with | ValueNone, ValueNone -> () | ValueSome prevVal, ValueSome newVal when prevVal = newVal -> () @@ -917,3 +953,90 @@ module Converters = let items = (sender :?> Xamarin.Forms.ListView).ItemsSource :?> System.Collections.Generic.IList tryFindGroupedListViewItemIndex items item | _ -> None + + let internal updateShellSearchHandler prevValueOpt (currValueOpt: ViewElement voption) target = + match prevValueOpt, currValueOpt with + | ValueNone, ValueNone -> () + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueSome prevValue, ValueSome currValue -> + let searchHandler = Shell.GetSearchHandler(target) + currValue.UpdateIncremental(prevValue, searchHandler) + | ValueNone, ValueSome currValue -> Shell.SetSearchHandler(target, currValue.Create() :?> Xamarin.Forms.SearchHandler) + | ValueSome _, ValueNone -> Shell.SetSearchHandler(target, null) + + let internal updateShellBackgroundColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetBackgroundColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetBackgroundColor(target, Color.Default) + + let internal updateShellForegroundColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetForegroundColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetForegroundColor(target, Color.Default) + + let internal updateShellTitleColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetTitleColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetTitleColor(target, Color.Default) + + let internal updateShellDisabledColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetDisabledColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetDisabledColor(target, Color.Default) + + let internal updateShellUnselectedColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetUnselectedColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetUnselectedColor(target, Color.Default) + + let internal updateShellTabBarBackgroundColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetTabBarBackgroundColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetTabBarBackgroundColor(target, Color.Default) + + let internal updateShellTabBarForegroundColor prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetTabBarForegroundColor(target, currValue) + | ValueSome _, ValueNone -> Shell.SetTabBarForegroundColor(target, Color.Default) + + let internal updateShellBackButtonBehavior prevValueOpt (currValueOpt: ViewElement voption) target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetBackButtonBehavior(target, currValue.Create() :?> BackButtonBehavior) + | ValueSome _, ValueNone -> Shell.SetBackButtonBehavior(target, null) + + let internal updateShellTitleView prevValueOpt (currValueOpt: ViewElement voption) target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetTitleView(target, currValue.Create() :?> View) + | ValueSome _, ValueNone -> Shell.SetTitleView(target, null) + + let internal updateShellFlyoutBehavior prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetFlyoutBehavior(target, currValue) + | ValueSome _, ValueNone -> Shell.SetFlyoutBehavior(target, FlyoutBehavior.Flyout) + + let internal updateShellTabBarIsVisible prevValueOpt currValueOpt target = + match prevValueOpt, currValueOpt with + | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> () + | ValueNone, ValueNone -> () + | _, ValueSome currValue -> Shell.SetTabBarIsVisible(target, currValue) + | ValueSome _, ValueNone -> Shell.SetTabBarIsVisible(target, true) \ No newline at end of file diff --git a/tools/Generator/CodeGenerator.fs b/tools/Generator/CodeGenerator.fs index 4f889fb0e..93f5b6ec5 100644 --- a/tools/Generator/CodeGenerator.fs +++ b/tools/Generator/CodeGenerator.fs @@ -23,6 +23,8 @@ module CodeGenerator = match m with | "ElementCreated" -> w.printfn " let ElementCreatedAttribKey : AttributeKey<(obj -> unit)> = AttributeKey<(obj -> unit)>(\"ElementCreated\")" + | "SearchHandlerItemSelected" -> // AttributeKey<_> incorrectly generalizes obj to 'a + w.printfn " let SearchHandlerItemSelectedAttribKey : AttributeKey<(obj -> unit)> = AttributeKey<(obj -> unit)>(\"SearchHandlerItemSelected\")" | _ -> w.printfn " let %sAttribKey : AttributeKey<_> = AttributeKey<_>(\"%s\")" m m w.printfn "" @@ -164,7 +166,8 @@ module CodeGenerator = match m.BoundType with // Check if the type of the member is in the model, if so issue recursive calls to "Create" and "UpdateIncremental" - | Some boundType when (tryFindType data.KnownTypes boundType.FullName).IsSome && not hasApply -> + // ModelType = "ViewElement" is also accepted because some properties (like FlyoutHeader) are typed object by default (thus disabling control reuse in the generator) + | Some boundType when ((tryFindType data.KnownTypes boundType.FullName).IsSome || m.ModelType = "ViewElement") && not hasApply -> w.printfn " match prev%sOpt, curr%sOpt with" m.UniqueName m.UniqueName w.printfn " // For structured objects, dependsOn on reference equality" w.printfn " | ValueSome prevValue, ValueSome newValue when identical prevValue newValue -> ()" diff --git a/tools/Generator/Resolvers.fs b/tools/Generator/Resolvers.fs index 8e430fd17..b27c92dc2 100644 --- a/tools/Generator/Resolvers.fs +++ b/tools/Generator/Resolvers.fs @@ -167,7 +167,10 @@ module Resolvers = let getModelType (this : MemberBinding, bindings : Bindings, memberResolutions, hierarchy : seq) = match this.ModelType with - | NotNullOrWhitespace s -> s + | NotNullOrWhitespace s -> + match (bindings.Types |> Seq.tryFind (fun x -> x.Name = s)) with + | None -> s + | Some _ -> "ViewElement" | _ -> match tryGetBoundType memberResolutions this with | None -> failwithf "no type for %s" this.Name diff --git a/tools/Generator/Xamarin.Forms.Core.json b/tools/Generator/Xamarin.Forms.Core.json index 1a78f1a68..461ceb7d5 100644 --- a/tools/Generator/Xamarin.Forms.Core.json +++ b/tools/Generator/Xamarin.Forms.Core.json @@ -33,6 +33,67 @@ "inputType": "ViewRef", "modelType": "ViewRef", "updateCode": "(fun _ _ _ -> ())" + }, + { + "name": "Tag", + "modelType": "obj", + "defaultValue": "null", + "updateCode": "(fun _ _ _ -> ())" + }, + { + "name": "ShellBackgroundColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellBackgroundColor" + }, + { + "name": "ShellForegroundColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellForegroundColor" + }, + { + "name": "ShellDisabledColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellDisabledColor" + }, + { + "name": "ShellTabBarBackgroundColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellTabBarBackgroundColor" + }, + { + "name": "ShellTabBarForegroundColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellTabBarForegroundColor" + }, + { + "name": "ShellTitleColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellTitleColor" + }, + { + "name": "ShellUnselectedColor", + "modelType": "Xamarin.Forms.Color", + "updateCode": "updateShellUnselectedColor" + }, + { + "name": "ShellBackButtonBehavior", + "modelType": "Xamarin.Forms.BackButtonBehavior", + "updateCode": "updateShellBackButtonBehavior" + }, + { + "name": "ShellFlyoutBehavior", + "modelType": "Xamarin.Forms.FlyoutBehavior", + "updateCode": "updateShellFlyoutBehavior" + }, + { + "name": "ShellTabBarIsVisible", + "modelType": "bool", + "updateCode": "updateShellTabBarIsVisible" + }, + { + "name": "ShellTitleView", + "modelType": "Xamarin.Forms.View", + "updateCode": "updateShellTitleView" } ] }, @@ -224,6 +285,18 @@ } ] }, + { + "name": "Xamarin.Forms.GestureElement", + "members": [ + { + "name": "GestureRecognizers", + "defaultValue": "null", + "inputType": "ViewElement list", + "modelType": "ViewElement[]", + "convToModel": "Array.ofList" + } + ] + }, { "name": "Xamarin.Forms.IGestureRecognizer", "members": [] @@ -1595,6 +1668,11 @@ "modelType": "obj", "convToValue": "makeImageSource", "defaultValue": "null" + }, + { + "name": "ShellSearchHandler", + "modelType": "Xamarin.Forms.SearchHandler", + "updateCode": "updateShellSearchHandler" } ] }, @@ -2216,6 +2294,7 @@ }, { "name": "Xamarin.Forms.SearchHandler", + "customType": "Fabulous.DynamicViews.CustomSearchHandler", "members": [ { "name": "ClearIcon", @@ -2334,7 +2413,12 @@ }, { "name": "ItemsSource", - "defaultValue": "null" + "uniqueName": "SearchHandlerItemsSource", + "shortName": "items", + "defaultValue": "null", + "inputType": "seq", + "modelType": "seq", + "updateCode": "updateSearchHandlerItems" }, { "name": "BackgroundColor", @@ -2407,6 +2491,25 @@ "inputType": "obj", "modelType": "obj", "updateCode": "updateSelectedItem" + }, + { + "name": "QueryChanged", + "defaultValue": "ignore", + "modelType": "string * string -> unit", + "updateCode": "(fun _ curr (target: Xamarin.Forms.SearchHandler) -> match curr with ValueSome fn -> (target :?> Fabulous.DynamicViews.CustomSearchHandler).QueryChanged <- fn | ValueNone -> ())" + }, + { + "name": "QueryConfirmed", + "defaultValue": "ignore", + "modelType": "unit -> unit", + "updateCode": "(fun _ curr (target: Xamarin.Forms.SearchHandler) -> match curr with ValueSome fn -> (target :?> Fabulous.DynamicViews.CustomSearchHandler).QueryConfirmed <- fn | ValueNone -> ())" + }, + { + "name": "ItemSelected", + "uniqueName": "SearchHandlerItemSelected", + "defaultValue": "ignore", + "modelType": "obj -> unit", + "updateCode": "(fun _ curr (target: Xamarin.Forms.SearchHandler) -> match curr with ValueSome fn -> (target :?> Fabulous.DynamicViews.CustomSearchHandler).ItemSelected <- fn | ValueNone -> ())" } ] }, @@ -2453,7 +2556,7 @@ "defaultValue": "null", "inputType": "bool", "modelType": "bool", - "updateCode": "updateIsCecked" + "updateCode": "updateIsChecked" } ] }, @@ -2483,7 +2586,8 @@ }, { "name": "FlyoutHeader", - "defaultValue": "null" + "defaultValue": "null", + "modelType": "ViewElement" }, { "name": "FlyoutHeaderBehavior", @@ -2551,16 +2655,10 @@ "inputType": "unit -> unit", "convToModel": "makeCommand" }, - { - "name": "SelectionChangedCommandParameter", - "defaultValue": "null" - }, { "name": "SelectionMode", - "uniqueName": "selectableItemsMode", - "defaultValue": "Xamarin.Forms.SelectionMode.None", - "inputType": "Xamarin.Forms.SelectionMode", - "modelType": "Xamarin.Forms.SelectionMode" + "uniqueName": "SelectableItemsViewSelectionMode", + "defaultValue": "Xamarin.Forms.SelectionMode.None" }, { "name": "SelectionChanged", @@ -2575,11 +2673,8 @@ "members": [ { "name": "Content", - "uniqueName": "ShellContent", "defaultValue": "null", - "inputType": "ViewElement", - "modelType": "ViewElement", - "updateCode": "(fun prev (curr: ViewElement voption) (target: Xamarin.Forms.ShellContent) -> target.Content <- match curr with ValueSome c -> c.Create() | ValueNone -> null)" + "modelType": "ViewElement" }, { "name": "MenuItems",