From 6eaa99a35aa23b24bec32e0da092ac479f452e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9=20Larivi=C3=A8re?= Date: Sun, 30 Jun 2024 17:28:35 +0200 Subject: [PATCH 1/4] Split Attributes to Mvu & Components --- src/Fabulous/Attributes.Components.fs | 51 +++++++++++++ src/Fabulous/Attributes.Mvu.fs | 67 ++++++++++++++++ src/Fabulous/Attributes.fs | 105 -------------------------- src/Fabulous/Fabulous.fsproj | 2 + 4 files changed, 120 insertions(+), 105 deletions(-) create mode 100644 src/Fabulous/Attributes.Components.fs create mode 100644 src/Fabulous/Attributes.Mvu.fs diff --git a/src/Fabulous/Attributes.Components.fs b/src/Fabulous/Attributes.Components.fs new file mode 100644 index 000000000..ff46d311f --- /dev/null +++ b/src/Fabulous/Attributes.Components.fs @@ -0,0 +1,51 @@ +namespace Fabulous + +open System +open Fabulous +open Fabulous.ScalarAttributeDefinitions + +module ComponentAttributes = + /// Define an attribute for EventHandler + let inline defineEventNoArg name ([] getEvent: obj -> IEvent) : SimpleScalarAttributeDefinition unit> = + let key = + SimpleScalarAttributeDefinition.CreateAttributeData( + ScalarAttributeComparers.noCompare, + (fun _ (newValueOpt: (unit -> unit) voption) node -> + match node.TryGetHandler(name) with + | ValueNone -> () + | ValueSome handler -> handler.Dispose() + + match newValueOpt with + | ValueNone -> node.RemoveHandler(name) + | ValueSome(fn) -> + let event = getEvent node.Target + node.SetHandler(name, event.Subscribe(fun _ -> fn()))) + ) + + |> AttributeDefinitionStore.registerScalar + + { Key = key; Name = name } + + /// Define an attribute for EventHandler<'T> + let inline defineEvent<'args> + name + ([] getEvent: obj -> IEvent, 'args>) + : SimpleScalarAttributeDefinition<'args -> unit> = + let key = + SimpleScalarAttributeDefinition.CreateAttributeData( + ScalarAttributeComparers.noCompare, + (fun _ (newValueOpt: ('args -> unit) voption) node -> + match node.TryGetHandler(name) with + | ValueNone -> () + | ValueSome handler -> handler.Dispose() + + match newValueOpt with + | ValueNone -> node.RemoveHandler(name) + | ValueSome(fn) -> + let event = getEvent node.Target + node.SetHandler(name, event.Subscribe(fun args -> fn args))) + ) + + |> AttributeDefinitionStore.registerScalar + + { Key = key; Name = name } diff --git a/src/Fabulous/Attributes.Mvu.fs b/src/Fabulous/Attributes.Mvu.fs new file mode 100644 index 000000000..972ac0a23 --- /dev/null +++ b/src/Fabulous/Attributes.Mvu.fs @@ -0,0 +1,67 @@ +namespace Fabulous + +open System +open System.Runtime.CompilerServices +open Fabulous +open Fabulous.ScalarAttributeDefinitions + +type MsgValue = MsgValue of obj + +[] +type SimpleScalarAttributeDefinitionExtensions() = + [] + static member inline WithValue(this: SimpleScalarAttributeDefinition<'args -> MsgValue>, value: 'args -> 'msg) = + this.WithValue(value >> box >> MsgValue) + +module MvuAttributes = + + /// Define an attribute for EventHandler + let inline defineEventNoArg name ([] getEvent: obj -> IEvent) : SimpleScalarAttributeDefinition = + let key = + SimpleScalarAttributeDefinition.CreateAttributeData( + ScalarAttributeComparers.noCompare, + (fun _ (newValueOpt: MsgValue voption) node -> + match node.TryGetHandler(name) with + | ValueNone -> () + | ValueSome handler -> handler.Dispose() + + match newValueOpt with + | ValueNone -> node.RemoveHandler(name) + | ValueSome(MsgValue msg) -> + let event = getEvent node.Target + let handler = event.Subscribe(fun _ -> Dispatcher.dispatch node msg) + node.SetHandler(name, handler)) + ) + + |> AttributeDefinitionStore.registerScalar + + { Key = key; Name = name } + + /// Define an attribute for EventHandler<'T> + let inline defineEvent<'args> + name + ([] getEvent: obj -> IEvent, 'args>) + : SimpleScalarAttributeDefinition<'args -> MsgValue> = + let key = + SimpleScalarAttributeDefinition.CreateAttributeData( + ScalarAttributeComparers.noCompare, + (fun _ (newValueOpt: ('args -> MsgValue) voption) (node: IViewNode) -> + match node.TryGetHandler(name) with + | ValueNone -> () + | ValueSome handler -> handler.Dispose() + + match newValueOpt with + | ValueNone -> node.RemoveHandler(name) + | ValueSome fn -> + let event = getEvent node.Target + + let handler = + event.Subscribe(fun args -> + let (MsgValue r) = fn args + Dispatcher.dispatch node r) + + node.SetHandler(name, handler)) + ) + |> AttributeDefinitionStore.registerScalar + + { Key = key; Name = name } diff --git a/src/Fabulous/Attributes.fs b/src/Fabulous/Attributes.fs index a272fe763..5209f03ad 100644 --- a/src/Fabulous/Attributes.fs +++ b/src/Fabulous/Attributes.fs @@ -84,14 +84,6 @@ type SmallScalarExtensions() = ) = this.WithValue(value, SmallScalars.IntEnum.encode) -type MsgValue = MsgValue of obj - -[] -type SimpleScalarAttributeDefinitionExtensions() = - [] - static member inline WithValue(this: SimpleScalarAttributeDefinition<'args -> MsgValue>, value: 'args -> 'msg) = - this.WithValue(value >> box >> MsgValue) - module Attributes = /// Define an attribute that can fit into 8 bytes encoded as uint64 (such as float or bool) let inline defineSmallScalar<'T> @@ -281,100 +273,3 @@ module Attributes = |> AttributeDefinitionStore.registerScalar { Key = key; Name = name } - - /// Define an attribute for EventHandler - let inline defineEventNoArg name ([] getEvent: obj -> IEvent) : SimpleScalarAttributeDefinition = - let key = - SimpleScalarAttributeDefinition.CreateAttributeData( - ScalarAttributeComparers.noCompare, - (fun _ (newValueOpt: MsgValue voption) node -> - match node.TryGetHandler(name) with - | ValueNone -> () - | ValueSome handler -> handler.Dispose() - - match newValueOpt with - | ValueNone -> node.RemoveHandler(name) - | ValueSome(MsgValue msg) -> - let event = getEvent node.Target - let handler = event.Subscribe(fun _ -> Dispatcher.dispatch node msg) - node.SetHandler(name, handler)) - ) - - |> AttributeDefinitionStore.registerScalar - - { Key = key; Name = name } - - /// Define an attribute for EventHandler<'T> - let inline defineEvent<'args> - name - ([] getEvent: obj -> IEvent, 'args>) - : SimpleScalarAttributeDefinition<'args -> MsgValue> = - let key = - SimpleScalarAttributeDefinition.CreateAttributeData( - ScalarAttributeComparers.noCompare, - (fun _ (newValueOpt: ('args -> MsgValue) voption) (node: IViewNode) -> - match node.TryGetHandler(name) with - | ValueNone -> () - | ValueSome handler -> handler.Dispose() - - match newValueOpt with - | ValueNone -> node.RemoveHandler(name) - | ValueSome fn -> - let event = getEvent node.Target - - let handler = - event.Subscribe(fun args -> - let (MsgValue r) = fn args - Dispatcher.dispatch node r) - - node.SetHandler(name, handler)) - ) - |> AttributeDefinitionStore.registerScalar - - { Key = key; Name = name } - - let inline defineEventNoArgNoDispatch - name - ([] getEvent: obj -> IEvent) - : SimpleScalarAttributeDefinition unit> = - let key = - SimpleScalarAttributeDefinition.CreateAttributeData( - ScalarAttributeComparers.noCompare, - (fun _ (newValueOpt: (unit -> unit) voption) node -> - match node.TryGetHandler(name) with - | ValueNone -> () - | ValueSome handler -> handler.Dispose() - - match newValueOpt with - | ValueNone -> node.RemoveHandler(name) - | ValueSome(fn) -> - let event = getEvent node.Target - node.SetHandler(name, event.Subscribe(fun _ -> fn()))) - ) - - |> AttributeDefinitionStore.registerScalar - - { Key = key; Name = name } - - let inline defineEventNoDispatch<'args> - name - ([] getEvent: obj -> IEvent, 'args>) - : SimpleScalarAttributeDefinition<'args -> unit> = - let key = - SimpleScalarAttributeDefinition.CreateAttributeData( - ScalarAttributeComparers.noCompare, - (fun _ (newValueOpt: ('args -> unit) voption) node -> - match node.TryGetHandler(name) with - | ValueNone -> () - | ValueSome handler -> handler.Dispose() - - match newValueOpt with - | ValueNone -> node.RemoveHandler(name) - | ValueSome(fn) -> - let event = getEvent node.Target - node.SetHandler(name, event.Subscribe(fun args -> fn args))) - ) - - |> AttributeDefinitionStore.registerScalar - - { Key = key; Name = name } diff --git a/src/Fabulous/Fabulous.fsproj b/src/Fabulous/Fabulous.fsproj index 313be82b4..532197f33 100644 --- a/src/Fabulous/Fabulous.fsproj +++ b/src/Fabulous/Fabulous.fsproj @@ -40,6 +40,8 @@ + + From fbc9b1fcdae151bdb64a8f444b03d2d8f7494aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9=20Larivi=C3=A8re?= Date: Sun, 30 Jun 2024 19:22:34 +0200 Subject: [PATCH 2/4] Split lifecycle --- src/Fabulous/Attributes.Components.fs | 66 +++++++++++++++++++++++++++ src/Fabulous/Attributes.Mvu.fs | 66 +++++++++++++++++++++++++++ src/Fabulous/Attributes.fs | 66 --------------------------- src/Fabulous/Lifecycle.fs | 16 ++++++- 4 files changed, 147 insertions(+), 67 deletions(-) diff --git a/src/Fabulous/Attributes.Components.fs b/src/Fabulous/Attributes.Components.fs index ff46d311f..16f13bc04 100644 --- a/src/Fabulous/Attributes.Components.fs +++ b/src/Fabulous/Attributes.Components.fs @@ -5,6 +5,72 @@ open Fabulous open Fabulous.ScalarAttributeDefinitions module ComponentAttributes = + /// Define an attribute storing a collection of Widget for a List property + let inline defineListWidgetCollection<'itemType> name ([] getCollection: obj -> System.Collections.Generic.IList<'itemType>) = + let applyDiff _ (diffs: WidgetCollectionItemChanges) (node: IViewNode) = + let targetColl = getCollection node.Target + + for diff in diffs do + match diff with + | WidgetCollectionItemChange.Remove(index, widget) -> + let itemNode = node.TreeContext.GetViewNode(box targetColl[index]) + + // Trigger the unmounted event + // TODO: Trigger Unmounted function for all children - Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Unmounted + itemNode.Dispose() + + // Remove the child from the UI tree + targetColl.RemoveAt(index) + + | _ -> () + + for diff in diffs do + match diff with + | WidgetCollectionItemChange.Insert(index, widget) -> + let struct (itemNode, view) = Helpers.createViewForWidget node widget + + // Insert the new child into the UI tree + targetColl.Insert(index, unbox view) + + // Trigger the mounted event + // TODO: Trigger Mounted function for all children - Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Mounted + + | WidgetCollectionItemChange.Update(index, widgetDiff) -> + let childNode = node.TreeContext.GetViewNode(box targetColl[index]) + + childNode.ApplyDiff(&widgetDiff) + + | WidgetCollectionItemChange.Replace(index, oldWidget, newWidget) -> + let prevItemNode = node.TreeContext.GetViewNode(box targetColl[index]) + + let struct (nextItemNode, view) = Helpers.createViewForWidget node newWidget + + // Trigger the unmounted event for the old child + // TODO: Trigger Unmounted function for all children - Dispatcher.dispatchEventForAllChildren prevItemNode oldWidget Lifecycle.Unmounted + prevItemNode.Dispose() + + // Replace the existing child in the UI tree at the index with the new one + targetColl[index] <- unbox view + + // Trigger the mounted event for the new child + // TODO: Trigger Mounted function for all children - Dispatcher.dispatchEventForAllChildren nextItemNode newWidget Lifecycle.Mounted + + | _ -> () + + let updateNode _ (newValueOpt: ArraySlice voption) (node: IViewNode) = + let targetColl = getCollection node.Target + targetColl.Clear() + + match newValueOpt with + | ValueNone -> () + | ValueSome widgets -> + for widget in ArraySlice.toSpan widgets do + let struct (_, view) = Helpers.createViewForWidget node widget + + targetColl.Add(unbox view) + + Attributes.defineWidgetCollection name applyDiff updateNode + /// Define an attribute for EventHandler let inline defineEventNoArg name ([] getEvent: obj -> IEvent) : SimpleScalarAttributeDefinition unit> = let key = diff --git a/src/Fabulous/Attributes.Mvu.fs b/src/Fabulous/Attributes.Mvu.fs index 972ac0a23..c7403c42c 100644 --- a/src/Fabulous/Attributes.Mvu.fs +++ b/src/Fabulous/Attributes.Mvu.fs @@ -15,6 +15,72 @@ type SimpleScalarAttributeDefinitionExtensions() = module MvuAttributes = + /// Define an attribute storing a collection of Widget for a List property + let inline defineListWidgetCollection<'itemType> name ([] getCollection: obj -> System.Collections.Generic.IList<'itemType>) = + let applyDiff _ (diffs: WidgetCollectionItemChanges) (node: IViewNode) = + let targetColl = getCollection node.Target + + for diff in diffs do + match diff with + | WidgetCollectionItemChange.Remove(index, widget) -> + let itemNode = node.TreeContext.GetViewNode(box targetColl[index]) + + // Trigger the unmounted event + Dispatcher.dispatchEventForAllChildren itemNode widget MvuLifecycle.Unmounted + itemNode.Dispose() + + // Remove the child from the UI tree + targetColl.RemoveAt(index) + + | _ -> () + + for diff in diffs do + match diff with + | WidgetCollectionItemChange.Insert(index, widget) -> + let struct (itemNode, view) = Helpers.createViewForWidget node widget + + // Insert the new child into the UI tree + targetColl.Insert(index, unbox view) + + // Trigger the mounted event + Dispatcher.dispatchEventForAllChildren itemNode widget MvuLifecycle.Mounted + + | WidgetCollectionItemChange.Update(index, widgetDiff) -> + let childNode = node.TreeContext.GetViewNode(box targetColl[index]) + + childNode.ApplyDiff(&widgetDiff) + + | WidgetCollectionItemChange.Replace(index, oldWidget, newWidget) -> + let prevItemNode = node.TreeContext.GetViewNode(box targetColl[index]) + + let struct (nextItemNode, view) = Helpers.createViewForWidget node newWidget + + // Trigger the unmounted event for the old child + Dispatcher.dispatchEventForAllChildren prevItemNode oldWidget MvuLifecycle.Unmounted + prevItemNode.Dispose() + + // Replace the existing child in the UI tree at the index with the new one + targetColl[index] <- unbox view + + // Trigger the mounted event for the new child + Dispatcher.dispatchEventForAllChildren nextItemNode newWidget MvuLifecycle.Mounted + + | _ -> () + + let updateNode _ (newValueOpt: ArraySlice voption) (node: IViewNode) = + let targetColl = getCollection node.Target + targetColl.Clear() + + match newValueOpt with + | ValueNone -> () + | ValueSome widgets -> + for widget in ArraySlice.toSpan widgets do + let struct (_, view) = Helpers.createViewForWidget node widget + + targetColl.Add(unbox view) + + Attributes.defineWidgetCollection name applyDiff updateNode + /// Define an attribute for EventHandler let inline defineEventNoArg name ([] getEvent: obj -> IEvent) : SimpleScalarAttributeDefinition = let key = diff --git a/src/Fabulous/Attributes.fs b/src/Fabulous/Attributes.fs index 5209f03ad..4374e64f3 100644 --- a/src/Fabulous/Attributes.fs +++ b/src/Fabulous/Attributes.fs @@ -197,72 +197,6 @@ module Attributes = defineWidget name applyDiff updateNode - /// Define an attribute storing a collection of Widget for a List property - let inline defineListWidgetCollection<'itemType> name ([] getCollection: obj -> System.Collections.Generic.IList<'itemType>) = - let applyDiff _ (diffs: WidgetCollectionItemChanges) (node: IViewNode) = - let targetColl = getCollection node.Target - - for diff in diffs do - match diff with - | WidgetCollectionItemChange.Remove(index, widget) -> - let itemNode = node.TreeContext.GetViewNode(box targetColl[index]) - - // Trigger the unmounted event - Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Unmounted - itemNode.Dispose() - - // Remove the child from the UI tree - targetColl.RemoveAt(index) - - | _ -> () - - for diff in diffs do - match diff with - | WidgetCollectionItemChange.Insert(index, widget) -> - let struct (itemNode, view) = Helpers.createViewForWidget node widget - - // Insert the new child into the UI tree - targetColl.Insert(index, unbox view) - - // Trigger the mounted event - Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Mounted - - | WidgetCollectionItemChange.Update(index, widgetDiff) -> - let childNode = node.TreeContext.GetViewNode(box targetColl[index]) - - childNode.ApplyDiff(&widgetDiff) - - | WidgetCollectionItemChange.Replace(index, oldWidget, newWidget) -> - let prevItemNode = node.TreeContext.GetViewNode(box targetColl[index]) - - let struct (nextItemNode, view) = Helpers.createViewForWidget node newWidget - - // Trigger the unmounted event for the old child - Dispatcher.dispatchEventForAllChildren prevItemNode oldWidget Lifecycle.Unmounted - prevItemNode.Dispose() - - // Replace the existing child in the UI tree at the index with the new one - targetColl[index] <- unbox view - - // Trigger the mounted event for the new child - Dispatcher.dispatchEventForAllChildren nextItemNode newWidget Lifecycle.Mounted - - | _ -> () - - let updateNode _ (newValueOpt: ArraySlice voption) (node: IViewNode) = - let targetColl = getCollection node.Target - targetColl.Clear() - - match newValueOpt with - | ValueNone -> () - | ValueSome widgets -> - for widget in ArraySlice.toSpan widgets do - let struct (_, view) = Helpers.createViewForWidget node widget - - targetColl.Add(unbox view) - - defineWidgetCollection name applyDiff updateNode - /// Define an attribute for a value supporting equality comparison let inline defineSimpleScalarWithEquality<'T when 'T: equality> name diff --git a/src/Fabulous/Lifecycle.fs b/src/Fabulous/Lifecycle.fs index 64d1092e0..d9c2ab131 100644 --- a/src/Fabulous/Lifecycle.fs +++ b/src/Fabulous/Lifecycle.fs @@ -2,7 +2,7 @@ namespace Fabulous open Fabulous.ScalarAttributeDefinitions -module Lifecycle = +module MvuLifecycle = let inline private createAttribute name : SimpleScalarAttributeDefinition = let key = SimpleScalarAttributeDefinition.CreateAttributeData((fun _ _ -> ScalarAttributeComparison.Identical), (fun _oldValueOpt _newValueOpt _node -> ())) @@ -15,3 +15,17 @@ module Lifecycle = /// Store an event that will be triggered when a Widget has been unmounted from the UI tree let Unmounted = createAttribute "Unmounted" + +module ComponentLifecycle = + let inline private createAttribute name : SimpleScalarAttributeDefinition unit> = + let key = + SimpleScalarAttributeDefinition.CreateAttributeData((fun _ _ -> ScalarAttributeComparison.Identical), (fun _oldValueOpt _newValueOpt _node -> ())) + |> AttributeDefinitionStore.registerScalar + + { Key = key; Name = name } + + /// Store an event that will be triggered when a Widget has been mounted in the UI tree + let Mounted = createAttribute "Mounted" + + /// Store an event that will be triggered when a Widget has been unmounted from the UI tree + let Unmounted = createAttribute "Unmounted" \ No newline at end of file From b60f3ab5beb10a516ea625ce91e0a6a8b14c25ce Mon Sep 17 00:00:00 2001 From: edgarfgp Date: Sun, 7 Jul 2024 17:12:22 +0100 Subject: [PATCH 3/4] format code --- src/Fabulous/Attributes.Components.fs | 10 +++++----- src/Fabulous/Lifecycle.fs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Fabulous/Attributes.Components.fs b/src/Fabulous/Attributes.Components.fs index 16f13bc04..510d5779e 100644 --- a/src/Fabulous/Attributes.Components.fs +++ b/src/Fabulous/Attributes.Components.fs @@ -32,8 +32,8 @@ module ComponentAttributes = // Insert the new child into the UI tree targetColl.Insert(index, unbox view) - // Trigger the mounted event - // TODO: Trigger Mounted function for all children - Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Mounted + // Trigger the mounted event + // TODO: Trigger Mounted function for all children - Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Mounted | WidgetCollectionItemChange.Update(index, widgetDiff) -> let childNode = node.TreeContext.GetViewNode(box targetColl[index]) @@ -52,8 +52,8 @@ module ComponentAttributes = // Replace the existing child in the UI tree at the index with the new one targetColl[index] <- unbox view - // Trigger the mounted event for the new child - // TODO: Trigger Mounted function for all children - Dispatcher.dispatchEventForAllChildren nextItemNode newWidget Lifecycle.Mounted + // Trigger the mounted event for the new child + // TODO: Trigger Mounted function for all children - Dispatcher.dispatchEventForAllChildren nextItemNode newWidget Lifecycle.Mounted | _ -> () @@ -70,7 +70,7 @@ module ComponentAttributes = targetColl.Add(unbox view) Attributes.defineWidgetCollection name applyDiff updateNode - + /// Define an attribute for EventHandler let inline defineEventNoArg name ([] getEvent: obj -> IEvent) : SimpleScalarAttributeDefinition unit> = let key = diff --git a/src/Fabulous/Lifecycle.fs b/src/Fabulous/Lifecycle.fs index d9c2ab131..8018a38c5 100644 --- a/src/Fabulous/Lifecycle.fs +++ b/src/Fabulous/Lifecycle.fs @@ -28,4 +28,4 @@ module ComponentLifecycle = let Mounted = createAttribute "Mounted" /// Store an event that will be triggered when a Widget has been unmounted from the UI tree - let Unmounted = createAttribute "Unmounted" \ No newline at end of file + let Unmounted = createAttribute "Unmounted" From 7b572bda26a6f05ea33c59afd141451ca5dea0a7 Mon Sep 17 00:00:00 2001 From: edgarfgp Date: Sun, 7 Jul 2024 17:22:15 +0100 Subject: [PATCH 4/4] Update to net8 and fix tests --- src/Fabulous.Benchmarks/Fabulous.Benchmarks.fsproj | 2 +- src/Fabulous.Tests/APISketchTests/TestUI.Attributes.fs | 2 +- src/Fabulous.Tests/Fabulous.Tests.fsproj | 2 +- src/Fabulous/Fabulous.fsproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Fabulous.Benchmarks/Fabulous.Benchmarks.fsproj b/src/Fabulous.Benchmarks/Fabulous.Benchmarks.fsproj index 97dcd03d0..dda527a98 100644 --- a/src/Fabulous.Benchmarks/Fabulous.Benchmarks.fsproj +++ b/src/Fabulous.Benchmarks/Fabulous.Benchmarks.fsproj @@ -1,6 +1,6 @@  - net7.0 + net8.0 Exe false diff --git a/src/Fabulous.Tests/APISketchTests/TestUI.Attributes.fs b/src/Fabulous.Tests/APISketchTests/TestUI.Attributes.fs index e37175654..66280892e 100644 --- a/src/Fabulous.Tests/APISketchTests/TestUI.Attributes.fs +++ b/src/Fabulous.Tests/APISketchTests/TestUI.Attributes.fs @@ -148,7 +148,7 @@ module TestUI_Attributes = module Container = let Children = - Attributes.defineListWidgetCollection "Container_Children" (fun target -> + ComponentAttributes.defineListWidgetCollection "Container_Children" (fun target -> (target :?> IContainer).Children :> System.Collections.Generic.IList<_>) let Tap = defineContainerTappable "Container_Tap" diff --git a/src/Fabulous.Tests/Fabulous.Tests.fsproj b/src/Fabulous.Tests/Fabulous.Tests.fsproj index d60baac45..c6ffcfb29 100644 --- a/src/Fabulous.Tests/Fabulous.Tests.fsproj +++ b/src/Fabulous.Tests/Fabulous.Tests.fsproj @@ -1,6 +1,6 @@ - net7.0 + net8.0 Library false diff --git a/src/Fabulous/Fabulous.fsproj b/src/Fabulous/Fabulous.fsproj index 532197f33..75669f223 100644 --- a/src/Fabulous/Fabulous.fsproj +++ b/src/Fabulous/Fabulous.fsproj @@ -1,6 +1,6 @@  - net7.0;netstandard2.1 + net8.0;netstandard2.1 Fabulous true