diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f00ea0..3513b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 No unreleased changes. +## [2.4.1] - 2023-11-26 + +### Added +- Added additional Any widgets to support Memo widget as a root +- Added itemsLayout modifier to CollectionView widget + ## [2.4.0] - 2023-11-22 ### Changed @@ -50,7 +56,8 @@ No unreleased changes. ### Changed - Fabulous.XamarinForms has moved from the Fabulous repository to its own repository: [https://github.com/fabulous-dev/Fabulous.XamarinForms](https://github.com/fabulous-dev/Fabulous.XamarinForms) -[unreleased]: https://github.com/fabulous-dev/Fabulous.XamarinForms/compare/2.4.0...HEAD +[unreleased]: https://github.com/fabulous-dev/Fabulous.XamarinForms/compare/2.4.1...HEAD +[2.4.1]: https://github.com/fabulous-dev/Fabulous.XamarinForms/releases/tag/2.4.1 [2.4.0]: https://github.com/fabulous-dev/Fabulous.XamarinForms/releases/tag/2.4.0 [2.3.0]: https://github.com/fabulous-dev/Fabulous.XamarinForms/releases/tag/2.3.0 [2.2.0]: https://github.com/fabulous-dev/Fabulous.XamarinForms/releases/tag/2.2.0 diff --git a/src/Fabulous.XamarinForms/Fabulous.XamarinForms.fsproj b/src/Fabulous.XamarinForms/Fabulous.XamarinForms.fsproj index d836639..62a5c6c 100644 --- a/src/Fabulous.XamarinForms/Fabulous.XamarinForms.fsproj +++ b/src/Fabulous.XamarinForms/Fabulous.XamarinForms.fsproj @@ -1,4 +1,4 @@ - + netstandard2.1 Fabulous.XamarinForms @@ -95,6 +95,13 @@ + + + + + + + diff --git a/src/Fabulous.XamarinForms/Program.fs b/src/Fabulous.XamarinForms/Program.fs index 0217aa4..2d51bca 100644 --- a/src/Fabulous.XamarinForms/Program.fs +++ b/src/Fabulous.XamarinForms/Program.fs @@ -2,12 +2,21 @@ open System open Fabulous +open Fabulous.Memo open Fabulous.ScalarAttributeDefinitions open Fabulous.WidgetCollectionAttributeDefinitions open Xamarin.Forms open System.Diagnostics module ViewHelpers = + let private tryGetSmallScalarValue (widget: Widget) (def: SmallScalarAttributeDefinition<'data>) = + match widget.ScalarAttributes with + | ValueNone -> ValueNone + | ValueSome scalarAttrs -> + match Array.tryFind (fun (attr: ScalarAttribute) -> attr.Key = def.Key) scalarAttrs with + | None -> ValueNone + | Some attr -> ValueSome(unbox<'data> attr.Value) + let private tryGetScalarValue (widget: Widget) (def: SimpleScalarAttributeDefinition<'data>) = match widget.ScalarAttributes with | ValueNone -> ValueNone @@ -34,6 +43,8 @@ module ViewHelpers = if def.TargetType <> null then if def.TargetType.IsAssignableFrom(typeof) then canReuseNavigationPage prev curr + elif def.TargetType.IsAssignableFrom(typeof) then + canReuseItemsLayout prev curr else true else @@ -73,6 +84,13 @@ module ViewHelpers = | _ -> true + /// Given the Orientation of the ItemsLayout is passed to the constructor, it can't be changed later. + /// If the orientation changes, a new ItemsLayout has to be created. + and private canReuseItemsLayout (prev: Widget) (curr: Widget) = + let prevOpt = tryGetSmallScalarValue prev ItemsLayout.Orientation + let currOpt = tryGetSmallScalarValue curr ItemsLayout.Orientation + prevOpt = currOpt + let defaultLogger () = let log (level, message) = let traceLevel = @@ -115,6 +133,14 @@ module Program = let statefulWithCmd (init: 'arg -> 'model * Cmd<'msg>) (update: 'msg -> 'model -> 'model * Cmd<'msg>) (view: 'model -> WidgetBuilder<'msg, #IApplication>) = define init update view + /// Create a program using an MVU loop. Add support for Cmd + let statefulWithCmdMemo + (init: 'arg -> 'model * Cmd<'msg>) + (update: 'msg -> 'model -> 'model * Cmd<'msg>) + (view: 'model -> WidgetBuilder<'msg, Memoized<#IApplication>>) + = + define init update view + /// Create a program using an MVU loop. Add support for CmdMsg let statefulWithCmdMsg (init: 'arg -> 'model * 'cmdMsg list) @@ -136,6 +162,16 @@ module Program = /// Start the program let startApplication (program: Program) : Application = startApplicationWithArgs () program + /// Start the program + let startApplicationWithArgsMemo (arg: 'arg) (program: Program<'arg, 'model, 'msg, Memoized<#IApplication>>) : Application = + let runner = Runners.create program + runner.Start(arg) + let adapter = ViewAdapters.create ViewNode.get runner + adapter.CreateView() |> unbox + + /// Start the program + let startApplicationMemo (program: Program>) : Application = startApplicationWithArgsMemo () program + /// Subscribe to external source of events. /// The subscription is called once - with the initial model, but can dispatch new messages at any time. let withSubscription (subscribe: 'model -> Cmd<'msg>) (program: Program<'arg, 'model, 'msg, 'marker>) = diff --git a/src/Fabulous.XamarinForms/Views/Any.fs b/src/Fabulous.XamarinForms/Views/Any.fs index 9397667..f3163d3 100644 --- a/src/Fabulous.XamarinForms/Views/Any.fs +++ b/src/Fabulous.XamarinForms/Views/Any.fs @@ -1,6 +1,7 @@ namespace Fabulous.XamarinForms open Fabulous +open Fabulous.Memo open Fabulous.XamarinForms [] @@ -11,10 +12,22 @@ module AnyBuilders = static member AnyView<'msg, 'marker when 'marker :> IView>(widget: WidgetBuilder<'msg, 'marker>) = WidgetBuilder<'msg, IView>(widget.Key, widget.Attributes) + /// Downcast to IView to allow to return different types of views in a single expression (e.g. if/else, match with pattern, etc.) + static member AnyView<'msg, 'marker when 'marker :> IView>(widget: WidgetBuilder<'msg, Memoized<'marker>>) = + WidgetBuilder<'msg, Memoized>(widget.Key, widget.Attributes) + /// Downcast to IPage to allow to return different types of pages in a single expression (e.g. if/else, match with pattern, etc.) static member AnyPage<'msg, 'marker when 'marker :> IPage>(widget: WidgetBuilder<'msg, 'marker>) = WidgetBuilder<'msg, IPage>(widget.Key, widget.Attributes) + /// Downcast to IPage to allow to return different types of pages in a single expression (e.g. if/else, match with pattern, etc.) + static member AnyPage<'msg, 'marker when 'marker :> IPage>(widget: WidgetBuilder<'msg, Memoized<'marker>>) = + WidgetBuilder<'msg, Memoized>(widget.Key, widget.Attributes) + /// Downcast to ICell to allow to return different types of cells in a single expression (e.g. if/else, match with pattern, etc.) static member AnyCell<'msg, 'marker when 'marker :> ICell>(widget: WidgetBuilder<'msg, 'marker>) = WidgetBuilder<'msg, ICell>(widget.Key, widget.Attributes) + + /// Downcast to ICell to allow to return different types of cells in a single expression (e.g. if/else, match with pattern, etc.) + static member AnyCell<'msg, 'marker when 'marker :> ICell>(widget: WidgetBuilder<'msg, Memoized<'marker>>) = + WidgetBuilder<'msg, Memoized>(widget.Key, widget.Attributes) diff --git a/src/Fabulous.XamarinForms/Views/Collections/CollectionView.fs b/src/Fabulous.XamarinForms/Views/Collections/CollectionView.fs index 0bcdf4f..189341b 100644 --- a/src/Fabulous.XamarinForms/Views/Collections/CollectionView.fs +++ b/src/Fabulous.XamarinForms/Views/Collections/CollectionView.fs @@ -5,54 +5,11 @@ open Fabulous open Xamarin.Forms type ICollectionView = - inherit IItemsView + inherit IReordableItemsView module CollectionView = let WidgetKey = Widgets.register() - let GroupedItemsSource = - Attributes.defineSimpleScalar - "CollectionView_GroupedItemsSource" - (fun a b -> ScalarAttributeComparers.equalityCompare a.OriginalItems b.OriginalItems) - (fun _ newValueOpt node -> - let collectionView = node.Target :?> CollectionView - - match newValueOpt with - | ValueNone -> - collectionView.IsGrouped <- false - collectionView.ClearValue(CollectionView.ItemsSourceProperty) - collectionView.ClearValue(CollectionView.GroupHeaderTemplateProperty) - collectionView.ClearValue(CollectionView.GroupFooterTemplateProperty) - collectionView.ClearValue(CollectionView.ItemTemplateProperty) - - | ValueSome value -> - collectionView.IsGrouped <- true - - collectionView.SetValue(CollectionView.ItemTemplateProperty, WidgetDataTemplateSelector(node, unbox >> value.ItemTemplate)) - - collectionView.SetValue(CollectionView.GroupHeaderTemplateProperty, WidgetDataTemplateSelector(node, unbox >> value.HeaderTemplate)) - - if value.FooterTemplate.IsSome then - collectionView.SetValue( - CollectionView.GroupFooterTemplateProperty, - WidgetDataTemplateSelector(node, unbox >> value.FooterTemplate.Value) - ) - - collectionView.SetValue(CollectionView.ItemsSourceProperty, value.OriginalItems)) - - let SelectionMode = - Attributes.defineBindableEnum CollectionView.SelectionModeProperty - - let Header = Attributes.defineBindableWidget CollectionView.HeaderProperty - - let Footer = Attributes.defineBindableWidget CollectionView.FooterProperty - - let ItemSizingStrategy = - Attributes.defineBindableEnum CollectionView.ItemSizingStrategyProperty - - let SelectionChanged = - Attributes.defineEvent "CollectionView_SelectionChanged" (fun target -> (target :?> CollectionView).SelectionChanged) - [] module CollectionViewBuilders = type Fabulous.XamarinForms.View with @@ -66,40 +23,12 @@ module CollectionViewBuilders = = WidgetHelpers.buildGroupItems<'msg, ICollectionView, 'groupData, 'itemData, 'groupMarker, 'itemMarker> CollectionView.WidgetKey - CollectionView.GroupedItemsSource + GroupableItemsView.GroupedItemsSource items [] type CollectionViewModifiers = - [] - static member inline selectionMode(this: WidgetBuilder<'msg, #ICollectionView>, value: SelectionMode) = - this.AddScalar(CollectionView.SelectionMode.WithValue(value)) - - [] - static member inline onSelectionChanged(this: WidgetBuilder<'msg, #ICollectionView>, onSelectionChanged: SelectionChangedEventArgs -> 'msg) = - this.AddScalar(CollectionView.SelectionChanged.WithValue(fun args -> onSelectionChanged args |> box)) - - [] - static member inline header<'msg, 'marker, 'contentMarker when 'marker :> ICollectionView and 'contentMarker :> IView> - ( - this: WidgetBuilder<'msg, 'marker>, - content: WidgetBuilder<'msg, 'contentMarker> - ) = - this.AddWidget(CollectionView.Header.WithValue(content.Compile())) - - [] - static member inline footer<'msg, 'marker, 'contentMarker when 'marker :> ICollectionView and 'contentMarker :> IView> - ( - this: WidgetBuilder<'msg, 'marker>, - content: WidgetBuilder<'msg, 'contentMarker> - ) = - this.AddWidget(CollectionView.Footer.WithValue(content.Compile())) - - [] - static member inline itemSizingStrategy(this: WidgetBuilder<'msg, #ICollectionView>, value: ItemSizingStrategy) = - this.AddScalar(CollectionView.ItemSizingStrategy.WithValue(value)) - /// Link a ViewRef to access the direct CollectionView control instance [] static member inline reference(this: WidgetBuilder<'msg, ICollectionView>, value: ViewRef) = diff --git a/src/Fabulous.XamarinForms/Views/Collections/GridItemsLayout.fs b/src/Fabulous.XamarinForms/Views/Collections/GridItemsLayout.fs new file mode 100644 index 0000000..5464ccf --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/GridItemsLayout.fs @@ -0,0 +1,66 @@ +namespace Fabulous.XamarinForms + +open System.Runtime.CompilerServices +open Fabulous +open Fabulous.ScalarAttributeDefinitions +open Xamarin.Forms + +type IGridItemsLayout = + inherit Fabulous.XamarinForms.IItemsLayout + +module GridItemsLayout = + let Span = Attributes.defineBindableInt GridItemsLayout.SpanProperty + + let WidgetKey = + Widgets.registerWithFactory(fun widget -> + let span = + match widget.ScalarAttributes with + | ValueNone -> ValueNone + | ValueSome attrs -> + match Array.tryFind (fun (attr: ScalarAttribute) -> attr.Key = Span.Key) attrs with + | None -> ValueNone + | Some attr -> ValueSome(SmallScalars.Int.decode attr.NumericValue) + + let orientation = + match widget.ScalarAttributes with + | ValueNone -> failwith "GridItemsLayout must have an orientation attribute" + | ValueSome attrs -> + match Array.tryFind (fun (attr: ScalarAttribute) -> attr.Key = ItemsLayout.Orientation.Key) attrs with + | None -> failwith "GridItemsLayout must have an orientation attribute" + | Some attr -> SmallScalars.IntEnum.decode attr.NumericValue + + match span with + | ValueNone -> GridItemsLayout(orientation) + | ValueSome span -> GridItemsLayout(span, orientation)) + + let HorizontalItemSpacing = + Attributes.defineBindableFloat GridItemsLayout.HorizontalItemSpacingProperty + + let VerticalItemSpacing = + Attributes.defineBindableFloat GridItemsLayout.VerticalItemSpacingProperty + +[] +module GridItemsBuilders = + type Fabulous.XamarinForms.View with + + static member inline GridItemsLayout(orientation: ItemsLayoutOrientation) = + WidgetBuilder<'msg, IGridItemsLayout>(GridItemsLayout.WidgetKey, ItemsLayout.Orientation.WithValue(orientation)) + +[] +type GridItemsLayoutExtensions = + [] + static member inline horizontalItemSpacing(this: WidgetBuilder<'msg, #IGridItemsLayout>, value: float) = + this.AddScalar(GridItemsLayout.HorizontalItemSpacing.WithValue(value)) + + [] + static member inline span(this: WidgetBuilder<'msg, #IGridItemsLayout>, value: int) = + this.AddScalar(GridItemsLayout.Span.WithValue(value)) + + [] + static member inline verticalItemSpacing(this: WidgetBuilder<'msg, #IGridItemsLayout>, value: float) = + this.AddScalar(GridItemsLayout.VerticalItemSpacing.WithValue(value)) + + /// Link a ViewRef to access the direct GridItemsLayout control instance + [] + static member inline reference(this: WidgetBuilder<'msg, IListView>, value: ViewRef) = + this.AddScalar(ViewRefAttributes.ViewRef.WithValue(value.Unbox)) diff --git a/src/Fabulous.XamarinForms/Views/Collections/LinearItemsLayout.fs b/src/Fabulous.XamarinForms/Views/Collections/LinearItemsLayout.fs new file mode 100644 index 0000000..db8ef1c --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/LinearItemsLayout.fs @@ -0,0 +1,42 @@ +namespace Fabulous.XamarinForms + +open System.Runtime.CompilerServices +open Fabulous +open Xamarin.Forms + +type ILinearItemsLayout = + inherit Fabulous.XamarinForms.IItemsLayout + +module LinearItemsLayout = + let WidgetKey = + Widgets.registerWithFactory(fun widget -> + let orientation = + match widget.ScalarAttributes with + | ValueNone -> failwith "LinearItemsLayout must have an orientation attribute" + | ValueSome attrs -> + match Array.tryFind (fun (attr: ScalarAttribute) -> attr.Key = ItemsLayout.Orientation.Key) attrs with + | None -> failwith "LinearItemsLayout must have an orientation attribute" + | Some attr -> SmallScalars.IntEnum.decode attr.NumericValue + + LinearItemsLayout(orientation)) + + let ItemSpacing = + Attributes.defineBindableWithEquality LinearItemsLayout.ItemSpacingProperty + +[] +module LinearItemsBuilders = + type Fabulous.XamarinForms.View with + + static member inline LinearItemsLayout(orientation: ItemsLayoutOrientation) = + WidgetBuilder<'msg, ILinearItemsLayout>(LinearItemsLayout.WidgetKey, ItemsLayout.Orientation.WithValue(orientation)) + +[] +type LinearItemsLayoutExtensions = + [] + static member inline itemSpacing(this: WidgetBuilder<'msg, #ILinearItemsLayout>, value: float) = + this.AddScalar(LinearItemsLayout.ItemSpacing.WithValue(value)) + + /// Link a ViewRef to access the direct LinearItemsLayout control instance + [] + static member inline reference(this: WidgetBuilder<'msg, IListView>, value: ViewRef) = + this.AddScalar(ViewRefAttributes.ViewRef.WithValue(value.Unbox)) diff --git a/src/Fabulous.XamarinForms/Views/Collections/_GroupableItemsView.fs b/src/Fabulous.XamarinForms/Views/Collections/_GroupableItemsView.fs new file mode 100644 index 0000000..87a058a --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/_GroupableItemsView.fs @@ -0,0 +1,39 @@ +namespace Fabulous.XamarinForms + +open Fabulous +open Fabulous.XamarinForms +open Xamarin.Forms + +type IGroupableItemsView = + inherit ISelectableItemsView + +module GroupableItemsView = + let GroupedItemsSource = + Attributes.defineSimpleScalar + "CollectionView_GroupedItemsSource" + (fun a b -> ScalarAttributeComparers.equalityCompare a.OriginalItems b.OriginalItems) + (fun _ newValueOpt node -> + let collectionView = node.Target :?> GroupableItemsView + + match newValueOpt with + | ValueNone -> + collectionView.IsGrouped <- false + collectionView.ClearValue(CollectionView.ItemsSourceProperty) + collectionView.ClearValue(CollectionView.GroupHeaderTemplateProperty) + collectionView.ClearValue(CollectionView.GroupFooterTemplateProperty) + collectionView.ClearValue(CollectionView.ItemTemplateProperty) + + | ValueSome value -> + collectionView.IsGrouped <- true + + collectionView.SetValue(CollectionView.ItemTemplateProperty, WidgetDataTemplateSelector(node, unbox >> value.ItemTemplate)) + + collectionView.SetValue(CollectionView.GroupHeaderTemplateProperty, WidgetDataTemplateSelector(node, unbox >> value.HeaderTemplate)) + + if value.FooterTemplate.IsSome then + collectionView.SetValue( + CollectionView.GroupFooterTemplateProperty, + WidgetDataTemplateSelector(node, unbox >> value.FooterTemplate.Value) + ) + + collectionView.SetValue(CollectionView.ItemsSourceProperty, value.OriginalItems)) diff --git a/src/Fabulous.XamarinForms/Views/Collections/_ItemsLayout.fs b/src/Fabulous.XamarinForms/Views/Collections/_ItemsLayout.fs new file mode 100644 index 0000000..b2ab724 --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/_ItemsLayout.fs @@ -0,0 +1,30 @@ +namespace Fabulous.XamarinForms + +open System.Runtime.CompilerServices +open Fabulous +open Fabulous.ScalarAttributeDefinitions +open Xamarin.Forms + +type IItemsLayout = + interface + end + +module ItemsLayout = + let Orientation: SmallScalarAttributeDefinition = + Attributes.defineSmallScalar "ItemsLayout_Orientation" SmallScalars.IntEnum.decode (fun _ _ _ -> ()) + + let SnapPointsAlignment = + Attributes.defineBindableEnum ItemsLayout.SnapPointsAlignmentProperty + + let SnapPointsType = + Attributes.defineBindableEnum ItemsLayout.SnapPointsTypeProperty + +[] +type ItemsLayoutModifiers = + [] + static member snapPointsAlignment(this: WidgetBuilder<'msg, #IItemsLayout>, value: SnapPointsAlignment) = + this.AddScalar(ItemsLayout.SnapPointsAlignment.WithValue(value)) + + [] + static member snapPointsType(this: WidgetBuilder<'msg, #IItemsLayout>, value: SnapPointsType) = + this.AddScalar(ItemsLayout.SnapPointsType.WithValue(value)) diff --git a/src/Fabulous.XamarinForms/Views/Collections/_ReorderableItemsView.fs b/src/Fabulous.XamarinForms/Views/Collections/_ReorderableItemsView.fs new file mode 100644 index 0000000..cf76e7f --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/_ReorderableItemsView.fs @@ -0,0 +1,4 @@ +namespace Fabulous.XamarinForms + +type IReordableItemsView = + inherit IGroupableItemsView diff --git a/src/Fabulous.XamarinForms/Views/Collections/_SelectableItemsView.fs b/src/Fabulous.XamarinForms/Views/Collections/_SelectableItemsView.fs new file mode 100644 index 0000000..ffb2ed1 --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/_SelectableItemsView.fs @@ -0,0 +1,25 @@ +namespace Fabulous.XamarinForms + +open System.Runtime.CompilerServices +open Xamarin.Forms +open Fabulous + +type ISelectableItemsView = + inherit IStructuredItemsView + +module SelectableItemsView = + let SelectionMode = + Attributes.defineBindableEnum CollectionView.SelectionModeProperty + + let SelectionChanged = + Attributes.defineEvent "CollectionView_SelectionChanged" (fun target -> (target :?> CollectionView).SelectionChanged) + +[] +type SelectableItemsView = + [] + static member inline selectionMode(this: WidgetBuilder<'msg, #ISelectableItemsView>, value: SelectionMode) = + this.AddScalar(SelectableItemsView.SelectionMode.WithValue(value)) + + [] + static member inline onSelectionChanged(this: WidgetBuilder<'msg, #ISelectableItemsView>, onSelectionChanged: SelectionChangedEventArgs -> 'msg) = + this.AddScalar(SelectableItemsView.SelectionChanged.WithValue(fun args -> onSelectionChanged args |> box)) diff --git a/src/Fabulous.XamarinForms/Views/Collections/_StructuredItemsView.fs b/src/Fabulous.XamarinForms/Views/Collections/_StructuredItemsView.fs new file mode 100644 index 0000000..bfc6dfd --- /dev/null +++ b/src/Fabulous.XamarinForms/Views/Collections/_StructuredItemsView.fs @@ -0,0 +1,53 @@ +namespace Fabulous.XamarinForms + +open System.Runtime.CompilerServices +open Fabulous +open Xamarin.Forms + +type IStructuredItemsView = + inherit IItemsView + +module StructuredItemsView = + let Footer = Attributes.defineBindableWidget StructuredItemsView.FooterProperty + + let Header = Attributes.defineBindableWidget StructuredItemsView.HeaderProperty + + let ItemsSizingStrategy = + Attributes.defineBindableEnum StructuredItemsView.ItemSizingStrategyProperty + + let ItemsLayout = + Attributes.defineBindableWidget StructuredItemsView.ItemsLayoutProperty + +[] +type StructuredItemsViewModifiers = + [] + static member inline footer<'msg, 'marker, 'contentMarker when 'marker :> IStructuredItemsView and 'contentMarker :> IView> + ( + this: WidgetBuilder<'msg, 'marker>, + content: WidgetBuilder<'msg, 'contentMarker> + ) = + this.AddWidget(StructuredItemsView.Footer.WithValue(content.Compile())) + + [] + static member inline header<'msg, 'marker, 'contentMarker when 'marker :> IStructuredItemsView and 'contentMarker :> IView> + ( + this: WidgetBuilder<'msg, 'marker>, + content: WidgetBuilder<'msg, 'contentMarker> + ) = + this.AddWidget(StructuredItemsView.Header.WithValue(content.Compile())) + + [] + static member inline itemsSizingStrategy<'msg, 'marker when 'marker :> IStructuredItemsView> + ( + this: WidgetBuilder<'msg, 'marker>, + value: ItemSizingStrategy + ) = + this.AddScalar(StructuredItemsView.ItemsSizingStrategy.WithValue(value)) + + [] + static member inline itemsLayout<'msg, 'marker, 'contentMarker when 'marker :> IStructuredItemsView and 'contentMarker :> Fabulous.XamarinForms.IItemsLayout> + ( + this: WidgetBuilder<'msg, 'marker>, + content: WidgetBuilder<'msg, 'contentMarker> + ) = + this.AddWidget(StructuredItemsView.ItemsLayout.WithValue(content.Compile())) diff --git a/src/Fabulous.XamarinForms/Widgets.fs b/src/Fabulous.XamarinForms/Widgets.fs index 2ddefb8..02da67e 100644 --- a/src/Fabulous.XamarinForms/Widgets.fs +++ b/src/Fabulous.XamarinForms/Widgets.fs @@ -15,7 +15,7 @@ type View = end module Widgets = - let registerWithAdditionalSetup<'T when 'T :> Xamarin.Forms.BindableObject and 'T: (new: unit -> 'T)> (additionalSetup: 'T -> IViewNode -> unit) = + let registerWithFactoryAndAdditionalSetup<'T when 'T :> Xamarin.Forms.BindableObject> (factory: Widget -> 'T) (additionalSetup: 'T -> IViewNode -> unit) = let key = WidgetDefinitionStore.getNextKey() let definition = @@ -26,7 +26,7 @@ module Widgets = fun (widget, treeContext, parentNode) -> treeContext.Logger.Debug("Creating view for {0}", typeof<'T>.Name) - let view = new 'T() + let view = factory widget let weakReference = WeakReference(view) let parentNode = @@ -66,8 +66,14 @@ module Widgets = WidgetDefinitionStore.set key definition key + let rec registerWithFactory<'T when 'T :> Xamarin.Forms.BindableObject> (factory: Widget -> 'T) = + registerWithFactoryAndAdditionalSetup<'T> factory (fun _ _ -> ()) + + let registerWithAdditionalSetup<'T when 'T :> Xamarin.Forms.BindableObject and 'T: (new: unit -> 'T)> (additionalSetup: 'T -> IViewNode -> unit) = + registerWithFactoryAndAdditionalSetup<'T> (fun _ -> new 'T()) additionalSetup + let register<'T when 'T :> Xamarin.Forms.BindableObject and 'T: (new: unit -> 'T)> () = - registerWithAdditionalSetup<'T>(fun _ _ -> ()) + registerWithFactory<'T>(fun _ -> new 'T()) module WidgetHelpers = let inline compileSeq (items: seq>) =