Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow superclass properties to update before inherited ones #746

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Fabulous.CodeGen/src/Fabulous.CodeGen/Binder/Binder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ module Binder =
BinderHelpers.bindMembers
bindingsCollection.AttachedProperties
(tryBindAttachedProperty logger containerTypeName assemblyTypeAttachedProperties) })
HasPriority = bindingsTypeProperty.HasPriority |> Option.defaultValue false
IsInherited = false }

/// Try to create a bound event binding from the bindings data only
Expand Down Expand Up @@ -243,7 +244,8 @@ module Binder =
AttachedProperties =
BinderHelpers.bindMembers
cd.AttachedProperties
(tryBindAttachedProperty logger containerTypeName assemblyTypeAttachedProperties) })
(tryBindAttachedProperty logger containerTypeName assemblyTypeAttachedProperties) })
HasPriority = bindingsTypeProperty.HasPriority |> Option.defaultValue false
IsInherited = false }
)

Expand Down
1 change: 1 addition & 0 deletions Fabulous.CodeGen/src/Fabulous.CodeGen/Binder/Models.fs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ module Models =
ConvertModelToValue: string
UpdateCode: string
CollectionData: BoundPropertyCollectionData option
HasPriority: bool
IsInherited: bool }
interface IBoundConstructorMember with
member this.ShortName = this.ShortName
Expand Down
100 changes: 54 additions & 46 deletions Fabulous.CodeGen/src/Fabulous.CodeGen/Generator/CodeGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,54 @@ module CodeGenerator =
w

let generateUpdateFunction (data: UpdateData) (w: StringWriter) =
let generateProperties (properties: UpdateProperty array) =
for p in properties do
let hasApply = not (System.String.IsNullOrWhiteSpace(p.ConvertModelToValue)) || not (System.String.IsNullOrWhiteSpace(p.UpdateCode))
let attributeKey = getAttributeKey p.CustomAttributeKey p.UniqueName

// Check if the property is a collection
match p.CollectionDataElementType with
| Some collectionDataElementType when not hasApply ->
w.printfn " ViewUpdaters.updateCollectionGeneric prev%sOpt curr%sOpt target.%s" p.UniqueName p.UniqueName p.Name
w.printfn " (fun (x: ViewElement) -> x.Create() :?> %s)" collectionDataElementType
w.printfn " (match registry.TryGetValue(%s.KeyValue) with true, func -> func | false, _ -> (fun _ _ _ -> ()))" attributeKey
w.printfn " ViewHelpers.canReuseView"
w.printfn " ViewHelpers.tryGetKey"
w.printfn " ViewUpdaters.updateChild"

| Some _ when hasApply ->
w.printfn " %s prev%sOpt curr%sOpt target" p.UpdateCode p.UniqueName p.UniqueName
w.printfn " (match registry.TryGetValue(%s.KeyValue) with true, func -> func | false, _ -> (fun _ _ _ -> ()))" attributeKey

| _ ->
// If the type is ViewElement, then it's a type from the model
// Issue recursive calls to "Create" and "UpdateIncremental"
if p.ModelType = "ViewElement" && not hasApply then
w.printfn " match prev%sOpt, curr%sOpt with" p.UniqueName p.UniqueName
w.printfn " // For structured objects, dependsOn on reference equality"
w.printfn " | ValueSome prevValue, ValueSome newValue when identical prevValue newValue -> ()"
w.printfn " | ValueSome prevValue, ValueSome newValue when canReuseView prevValue newValue ->"
w.printfn " newValue.UpdateIncremental(prevValue, target.%s)" p.Name
w.printfn " | _, ValueSome newValue ->"
w.printfn " target.%s <- (newValue.Create() :?> %s)" p.Name p.OriginalType
w.printfn " | ValueSome _, ValueNone ->"
w.printfn " target.%s <- null" p.Name
w.printfn " | ValueNone, ValueNone -> ()"

// Explicit update code
elif not (System.String.IsNullOrWhiteSpace(p.UpdateCode)) then
w.printfn " %s prev%sOpt curr%sOpt target" p.UpdateCode p.UniqueName p.UniqueName

else
w.printfn " match prev%sOpt, curr%sOpt with" p.UniqueName p.UniqueName
w.printfn " | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> ()"
w.printfn " | _, ValueSome currValue -> target.%s <- %s currValue" p.Name p.ConvertModelToValue
if p.DefaultValue = "" then
w.printfn " | ValueSome _, ValueNone -> target.ClearValue %s.%sProperty" data.FullName p.Name
else
w.printfn " | ValueSome _, ValueNone -> target.%s <- %s" p.Name p.DefaultValue
w.printfn " | ValueNone, ValueNone -> ()"

w.printfn " static member Update%s (registry: System.Collections.Generic.Dictionary<int, ViewElement voption -> ViewElement -> obj -> unit>, prevOpt: ViewElement voption, curr: ViewElement, target: %s) = " data.Name data.FullName

// Attached properties updaters
Expand Down Expand Up @@ -182,6 +230,11 @@ module CodeGenerator =
w.printfn " | ValueSome prevValue -> target.%s.RemoveHandler(prevValue)" e.Name
w.printfn " | ValueNone -> ()"

// Update priority properties
if data.PriorityProperties.Length > 0 then
w.printfn " // Update priority properties"
generateProperties data.PriorityProperties

// Update inherited members
if data.BaseName.IsSome then
w.printfn " // Update inherited members"
Expand All @@ -190,52 +243,7 @@ module CodeGenerator =
// Update properties
if data.Properties.Length > 0 then
w.printfn " // Update properties"
for p in data.Properties do
let hasApply = not (System.String.IsNullOrWhiteSpace(p.ConvertModelToValue)) || not (System.String.IsNullOrWhiteSpace(p.UpdateCode))
let attributeKey = getAttributeKey p.CustomAttributeKey p.UniqueName

// Check if the property is a collection
match p.CollectionDataElementType with
| Some collectionDataElementType when not hasApply ->
w.printfn " ViewUpdaters.updateCollectionGeneric prev%sOpt curr%sOpt target.%s" p.UniqueName p.UniqueName p.Name
w.printfn " (fun (x: ViewElement) -> x.Create() :?> %s)" collectionDataElementType
w.printfn " (match registry.TryGetValue(%s.KeyValue) with true, func -> func | false, _ -> (fun _ _ _ -> ()))" attributeKey
w.printfn " ViewHelpers.canReuseView"
w.printfn " ViewHelpers.tryGetKey"
w.printfn " ViewUpdaters.updateChild"

| Some _ when hasApply ->
w.printfn " %s prev%sOpt curr%sOpt target" p.UpdateCode p.UniqueName p.UniqueName
w.printfn " (match registry.TryGetValue(%s.KeyValue) with true, func -> func | false, _ -> (fun _ _ _ -> ()))" attributeKey

| _ ->
// If the type is ViewElement, then it's a type from the model
// Issue recursive calls to "Create" and "UpdateIncremental"
if p.ModelType = "ViewElement" && not hasApply then
w.printfn " match prev%sOpt, curr%sOpt with" p.UniqueName p.UniqueName
w.printfn " // For structured objects, dependsOn on reference equality"
w.printfn " | ValueSome prevValue, ValueSome newValue when identical prevValue newValue -> ()"
w.printfn " | ValueSome prevValue, ValueSome newValue when canReuseView prevValue newValue ->"
w.printfn " newValue.UpdateIncremental(prevValue, target.%s)" p.Name
w.printfn " | _, ValueSome newValue ->"
w.printfn " target.%s <- (newValue.Create() :?> %s)" p.Name p.OriginalType
w.printfn " | ValueSome _, ValueNone ->"
w.printfn " target.%s <- null" p.Name
w.printfn " | ValueNone, ValueNone -> ()"

// Explicit update code
elif not (System.String.IsNullOrWhiteSpace(p.UpdateCode)) then
w.printfn " %s prev%sOpt curr%sOpt target" p.UpdateCode p.UniqueName p.UniqueName

else
w.printfn " match prev%sOpt, curr%sOpt with" p.UniqueName p.UniqueName
w.printfn " | ValueSome prevValue, ValueSome currValue when prevValue = currValue -> ()"
w.printfn " | _, ValueSome currValue -> target.%s <- %s currValue" p.Name p.ConvertModelToValue
if p.DefaultValue = "" then
w.printfn " | ValueSome _, ValueNone -> target.ClearValue %s.%sProperty" data.FullName p.Name
else
w.printfn " | ValueSome _, ValueNone -> target.%s <- %s" p.Name p.DefaultValue
w.printfn " | ValueNone, ValueNone -> ()"
generateProperties data.Properties

// Subscribe event handlers
if data.Events.Length > 0 then
Expand Down
1 change: 1 addition & 0 deletions Fabulous.CodeGen/src/Fabulous.CodeGen/Generator/Models.fs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ module Models =
ImmediateMembers : UpdateMember array
Events: UpdateEvent array
Properties: UpdateProperty array
PriorityProperties: UpdateProperty array
PropertiesWithAttachedProperties: UpdatePropertyWithAttachedProperties array }

type ConstructData =
Expand Down
16 changes: 16 additions & 0 deletions Fabulous.CodeGen/src/Fabulous.CodeGen/Generator/Preparer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ module Preparer =

let updateProperties =
immediateProperties
|> Array.filter (fun p -> not p.HasPriority)
|> Array.map (fun p ->
{ Name = p.Name
UniqueName = p.UniqueName
CustomAttributeKey = p.CustomAttributeKey
DefaultValue = p.DefaultValue
OriginalType = p.OriginalType
ModelType = p.ModelType
ConvertModelToValue = p.ConvertModelToValue
UpdateCode = p.UpdateCode
CollectionDataElementType = p.CollectionData |> Option.map (fun c -> c.ElementType) })

let updatePriorityProperties =
immediateProperties
|> Array.filter (fun p -> p.HasPriority)
|> Array.map (fun p ->
{ Name = p.Name
UniqueName = p.UniqueName
Expand Down Expand Up @@ -107,6 +122,7 @@ module Preparer =
ImmediateMembers = immediateMembers
Events = updateEvents
Properties = updateProperties
PriorityProperties = updatePriorityProperties
PropertiesWithAttachedProperties = updatePropertiesWithAttachedProperties }

let toConstructData (boundType: BoundType) : ConstructData =
Expand Down
1 change: 1 addition & 0 deletions Fabulous.CodeGen/src/Fabulous.CodeGen/Models.fs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ module Models =
member val DefaultValue = None with get, set
member val ConvertModelToValue = None with get, set
member val UpdateCode = None with get, set
member val HasPriority : bool option = None with get, set

interface IConstructorMember with
member x.ShortName = x.ShortName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,8 @@
"shortName": "coldefs",
"inputType": "Fabulous.XamarinForms.InputTypes.Dimension list",
"defaultValue": "Xamarin.Forms.ColumnDefinitionCollection()",
"convertModelToValue": "ViewConverters.convertFabulousDimensionToXamarinFormsColumnDefinition"
"convertModelToValue": "ViewConverters.convertFabulousDimensionToXamarinFormsColumnDefinition",
"hasPriority": true
},
{
"source": "ColumnSpacing"
Expand All @@ -2093,7 +2094,8 @@
"shortName": "rowdefs",
"inputType": "Fabulous.XamarinForms.InputTypes.Dimension list",
"defaultValue": "Xamarin.Forms.RowDefinitionCollection()",
"convertModelToValue": "ViewConverters.convertFabulousDimensionToXamarinFormsRowDefinition"
"convertModelToValue": "ViewConverters.convertFabulousDimensionToXamarinFormsRowDefinition",
"hasPriority": true
},
{
"source": "RowSpacing"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel =
Expand All @@ -59,6 +60,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = "(fun _ _ _ -> ())"
CollectionData = None
HasPriority = false
IsInherited = false }

{ Name = "NameCanExecute"
Expand All @@ -74,6 +76,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = "ViewUpdaters.updateCommand prevUniqueNameOpt currUniqueNameOpt (fun _target -> ()) (fun (target: FullName) cmd -> target.Name <- cmd)"
CollectionData = None
HasPriority = false
IsInherited = false } |]

boundModel |> OptimizeCommands.apply |> should equal expectedBoundModel
Expand All @@ -95,6 +98,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -118,6 +122,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = "ViewUpdaters.updateNameProperty"
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -141,6 +146,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -164,6 +170,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel =
Expand All @@ -181,6 +188,7 @@ module XFOptimizerTests =
ConvertModelToValue = "ViewConverters.convertFabulousImageToXamarinFormsImageSource"
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

boundModel |> OptimizeImageSource.apply |> should equal expectedBoundModel
Expand All @@ -202,6 +210,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -225,6 +234,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = "ViewUpdaters.updateNameProperty"
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -248,6 +258,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -271,6 +282,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel =
Expand All @@ -288,6 +300,7 @@ module XFOptimizerTests =
ConvertModelToValue = "ViewConverters.convertFabulousMediaToXamarinFormsMediaSource"
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

boundModel |> OptimizeMediaSource.apply |> should equal expectedBoundModel
Expand All @@ -309,6 +322,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -332,6 +346,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = "ViewUpdaters.updateNameProperty"
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand All @@ -355,6 +370,7 @@ module XFOptimizerTests =
ConvertModelToValue = ""
UpdateCode = ""
CollectionData = None
HasPriority = false
IsInherited = false } |]

let expectedBoundModel = boundModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module XFOptimizer =
ConvertModelToValue = ""
UpdateCode = sprintf "ViewUpdaters.updateCommand prev%sOpt curr%sOpt (fun _target -> ()) (fun (target: %s) cmd -> target.%s <- cmd)" boundProperty.UniqueName boundProperty.UniqueName boundType.FullName boundProperty.Name
CollectionData = None
HasPriority = false
IsInherited = false }
|]

Expand Down