Skip to content

Commit

Permalink
Merge branch 'main' into release/3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
TimLariviere committed Nov 20, 2024
2 parents 8eeb945 + e45bdf4 commit 6fe5bbc
Show file tree
Hide file tree
Showing 12 changed files with 286 additions and 68 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

_No unreleased changes_

## [3.0.0-pre12] - 2024-11-20

### Added
- Add support for multiple environment modifiers on the same widget by @TimLariviere

## [3.0.0-pre11] - 2024-11-16

### Changed
Expand Down Expand Up @@ -172,7 +177,8 @@ _No unreleased changes_
### Changed
- Fabulous.XamarinForms & Fabulous.MauiControls have been moved been out of the Fabulous repository. Find them in their own repositories: [https://github.com/fabulous-dev/Fabulous.XamarinForms](https://github.com/fabulous-dev/Fabulous.XamarinForms) / [https://github.com/fabulous-dev/Fabulous.MauiControls](https://github.com/fabulous-dev/Fabulous.MauiControls)

[unreleased]: https://github.com/fabulous-dev/Fabulous/compare/3.0.0-pre11...HEAD
[unreleased]: https://github.com/fabulous-dev/Fabulous/compare/3.0.0-pre12...HEAD
[3.0.0-pre12]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre12
[3.0.0-pre11]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre11
[3.0.0-pre10]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre10
[3.0.0-pre9]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre9
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sdk": {
"version": "8.0.404"
}
}
2 changes: 1 addition & 1 deletion src/Fabulous.Tests/APISketchTests/APISketchTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ module Issue104 =
let ControlWidgetKey = Widgets.register<TestButton>()

let Control () =
WidgetBuilder<'msg, TestButtonMarker>(ControlWidgetKey, AttributesBundle(StackList.StackList.empty(), ValueNone, ValueNone))
WidgetBuilder<'msg, TestButtonMarker>(ControlWidgetKey)

[<System.Runtime.CompilerServices.Extension>]
type WidgetExtensions() =
Expand Down
5 changes: 2 additions & 3 deletions src/Fabulous.Tests/ViewTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ type ``View helper functions tests``() =
/// Test: https://github.com/fabulous-dev/Fabulous/pull/1037
[<Test>]
member _.``Mapping a WidgetBuilder with Unit Msg to another Msg is supported``() =
let widgetBuilder =
WidgetBuilder<unit, ITestControl>(TestControl.WidgetKey, AttributesBundle(StackList.empty(), ValueNone, ValueNone))
let widgetBuilder = WidgetBuilder<unit, ITestControl>(TestControl.WidgetKey)

let mapMsg (oldMsg: unit) = ChildMsg oldMsg

let mappedWidgetBuilder = View.map mapMsg widgetBuilder

Assert.AreEqual(widgetBuilder.Key, mappedWidgetBuilder.Key)

let struct (scalars, _, _) = mappedWidgetBuilder.Attributes
let struct (scalars, _, _, _) = mappedWidgetBuilder.Attributes
let scalars = StackList.toArray &scalars

Assert.AreEqual(1, scalars.Length)
Expand Down
1 change: 0 additions & 1 deletion src/Fabulous/AttributeDefinitions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ module AttributeDefinitionStore =
let private _scalars = ResizeArray<ScalarAttributeData>()
let private _smallScalars = ResizeArray<SmallScalarAttributeData>()
let private _widgets = ResizeArray<WidgetAttributeData>()

let private _widgetCollections = ResizeArray<WidgetCollectionAttributeData>()

let registerSmallScalar (data: SmallScalarAttributeData) : ScalarAttributeKey =
Expand Down
99 changes: 71 additions & 28 deletions src/Fabulous/Builders.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,39 @@ open Fabulous.StackAllocatedCollections
open Fabulous.StackAllocatedCollections.StackList
open Microsoft.FSharp.Core

type AttributesBundle = (struct (StackList<ScalarAttribute> * WidgetAttribute[] voption * WidgetCollectionAttribute[] voption))
type AttributesBundle = (struct (StackList<ScalarAttribute> * WidgetAttribute[] voption * WidgetCollectionAttribute[] voption * EnvironmentAttribute[] voption))

[<Struct; NoComparison; NoEquality>]
type WidgetBuilder<'msg, 'marker when 'msg: equality> =
struct
val Key: WidgetKey
val Attributes: AttributesBundle

new(key: WidgetKey) =
{ Key = key
Attributes = AttributesBundle(StackList.empty(), ValueNone, ValueNone, ValueNone) }

new(key: WidgetKey, attributes: AttributesBundle) = { Key = key; Attributes = attributes }

new(key: WidgetKey, scalar: ScalarAttribute) =
{ Key = key
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone) }
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone, ValueNone) }

new(key: WidgetKey, scalarA: ScalarAttribute, scalarB: ScalarAttribute) =
{ Key = key
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone) }
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone, ValueNone) }

new(key: WidgetKey, scalar1: ScalarAttribute, scalar2: ScalarAttribute, scalar3: ScalarAttribute) =
{ Key = key
Attributes = AttributesBundle(StackList.three(scalar1, scalar2, scalar3), ValueNone, ValueNone) }
Attributes = AttributesBundle(StackList.three(scalar1, scalar2, scalar3), ValueNone, ValueNone, ValueNone) }

new(key: WidgetKey, widget: WidgetAttribute) =
{ Key = key
Attributes = AttributesBundle(StackList.empty(), ValueSome [| widget |], ValueNone, ValueNone) }

[<EditorBrowsable(EditorBrowsableState.Never)>]
member x.Compile() : Widget =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

{ Key = x.Key
Expand All @@ -41,19 +49,23 @@ type WidgetBuilder<'msg, 'marker when 'msg: equality> =
ScalarAttributes =
match StackList.length &scalarAttributes with
| 0us -> ValueNone
| _ -> ValueSome(Array.sortInPlace (fun a -> a.Key) (StackList.toArray &scalarAttributes))
| _ -> ValueSome(Array.sortInPlace _.Key (StackList.toArray &scalarAttributes))

WidgetAttributes = ValueOption.map (Array.sortInPlace(fun a -> a.Key)) widgetAttributes
WidgetAttributes = ValueOption.map (Array.sortInPlace(_.Key)) widgetAttributes

WidgetCollectionAttributes = widgetCollectionAttributes |> ValueOption.map(Array.sortInPlace(_.Key))

WidgetCollectionAttributes = widgetCollectionAttributes |> ValueOption.map(Array.sortInPlace(fun a -> a.Key)) }
EnvironmentAttributes = environmentAttributes |> ValueOption.map(Array.sortInPlace(_.Key)) }

[<EditorBrowsable(EditorBrowsableState.Never)>]
member inline x.AddScalar(attr: ScalarAttribute) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

WidgetBuilder<'msg, 'marker>(x.Key, struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes))
WidgetBuilder<'msg, 'marker>(
x.Key,
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes, environmentAttributes)
)

[<EditorBrowsable(EditorBrowsableState.Never)>]
member inline x.AddOrReplaceScalar
Expand All @@ -62,26 +74,29 @@ type WidgetBuilder<'msg, 'marker when 'msg: equality> =
[<InlineIfLambda>] replaceWith: ScalarAttribute -> ScalarAttribute,
[<InlineIfLambda>] defaultWith: unit -> ScalarAttribute
) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

match StackList.tryFind(&scalarAttributes, (fun attr -> attr.Key = attrKey)) with
| ValueNone ->
let attr = defaultWith()

WidgetBuilder<'msg, 'marker>(x.Key, struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes))
WidgetBuilder<'msg, 'marker>(
x.Key,
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes, environmentAttributes)
)

| ValueSome attr ->
let newAttr = replaceWith attr

let newAttrs =
StackList.replace(&scalarAttributes, (fun attr -> attr.Key = attrKey), newAttr)

WidgetBuilder<'msg, 'marker>(x.Key, struct (newAttrs, widgetAttributes, widgetCollectionAttributes))
WidgetBuilder<'msg, 'marker>(x.Key, struct (newAttrs, widgetAttributes, widgetCollectionAttributes, environmentAttributes))

[<EditorBrowsable(EditorBrowsableState.Never)>]
member x.AddWidget(attr: WidgetAttribute) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

let attribs = widgetAttributes
Expand All @@ -95,11 +110,11 @@ type WidgetBuilder<'msg, 'marker when 'msg: equality> =
attribs2[attribs.Length] <- attr
attribs2

WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, ValueSome res, widgetCollectionAttributes))
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, ValueSome res, widgetCollectionAttributes, environmentAttributes))

[<EditorBrowsable(EditorBrowsableState.Never)>]
member x.AddWidgetCollection(attr: WidgetCollectionAttribute) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

let attribs = widgetCollectionAttributes
Expand All @@ -113,7 +128,32 @@ type WidgetBuilder<'msg, 'marker when 'msg: equality> =
attribs2[attribs.Length] <- attr
attribs2

WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, widgetAttributes, ValueSome res))
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, widgetAttributes, ValueSome res, environmentAttributes))

[<EditorBrowsable(EditorBrowsableState.Never)>]
member inline x.AddEnvironment(key: EnvironmentAttributeKey, value: obj) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

let attr =
{ Key = key
#if DEBUG
DebugName = $"Environment.{key}"
#endif
Value = value }

let attribs = environmentAttributes

let res =
match attribs with
| ValueNone -> [| attr |]
| ValueSome attribs ->
let attribs2 = Array.zeroCreate(attribs.Length + 1)
Array.blit attribs 0 attribs2 0 attribs.Length
attribs2[attribs.Length] <- attr
attribs2

WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, ValueSome res))
end


Expand All @@ -130,12 +170,12 @@ type CollectionBuilder<'msg, 'marker, 'itemMarker when 'msg: equality> =

new(widgetKey: WidgetKey, scalars: StackList<ScalarAttribute>, attr: WidgetCollectionAttributeDefinition) =
{ WidgetKey = widgetKey
Attributes = AttributesBundle(scalars, ValueNone, ValueNone)
Attributes = AttributesBundle(scalars, ValueNone, ValueNone, ValueNone)
Attr = attr }

new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition) =
{ WidgetKey = widgetKey
Attributes = AttributesBundle(StackList.empty(), ValueNone, ValueNone)
Attributes = AttributesBundle(StackList.empty(), ValueNone, ValueNone, ValueNone)
Attr = attr }

new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition, attributes: AttributesBundle) =
Expand All @@ -145,16 +185,16 @@ type CollectionBuilder<'msg, 'marker, 'itemMarker when 'msg: equality> =

new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition, scalar: ScalarAttribute) =
{ WidgetKey = widgetKey
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone)
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone, ValueNone)
Attr = attr }

new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition, scalarA: ScalarAttribute, scalarB: ScalarAttribute) =
{ WidgetKey = widgetKey
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone)
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone, ValueNone)
Attr = attr }

member inline x.Run(c: Content<'msg>) =
let struct (scalars, widgets, widgetCollections) = x.Attributes
let struct (scalars, widgets, widgetCollections, environments) = x.Attributes

let attrValue =
match MutStackArray1.toArraySlice &c.Widgets with
Expand All @@ -168,7 +208,7 @@ type CollectionBuilder<'msg, 'marker, 'itemMarker when 'msg: equality> =
| ValueNone -> ValueSome([| widgetCollAttr |])
| ValueSome widgetCollectionAttributes -> ValueSome(Array.appendOne widgetCollAttr widgetCollectionAttributes)

WidgetBuilder<'msg, 'marker>(x.WidgetKey, AttributesBundle(scalars, widgets, widgetCollections))
WidgetBuilder<'msg, 'marker>(x.WidgetKey, AttributesBundle(scalars, widgets, widgetCollections, environments))

member inline _.Combine(a: Content<'msg>, b: Content<'msg>) : Content<'msg> =
let res = MutStackArray1.combineMut(&a.Widgets, b.Widgets)
Expand All @@ -191,13 +231,13 @@ type CollectionBuilder<'msg, 'marker, 'itemMarker when 'msg: equality> =

[<EditorBrowsable(EditorBrowsableState.Never)>]
member inline x.AddScalar(attr: ScalarAttribute) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
x.Attributes

CollectionBuilder<'msg, 'marker, 'itemMarker>(
x.WidgetKey,
x.Attr,
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes)
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes, environmentAttributes)
)

end
Expand Down Expand Up @@ -251,7 +291,7 @@ type SingleChildBuilder<'msg, 'marker, 'childMarker when 'msg: equality> =
new(widgetKey: WidgetKey, attr: WidgetAttributeDefinition) =
{ WidgetKey = widgetKey
Attr = attr
AttributesBundle = AttributesBundle(StackList.empty(), ValueNone, ValueNone) }
AttributesBundle = AttributesBundle(StackList.empty(), ValueNone, ValueNone, ValueNone) }

new(widgetKey: WidgetKey, attr: WidgetAttributeDefinition, attributesBundle: AttributesBundle) =
{ WidgetKey = widgetKey
Expand All @@ -273,7 +313,9 @@ type SingleChildBuilder<'msg, 'marker, 'childMarker when 'msg: equality> =

member inline this.Run([<InlineIfLambda>] result: SingleChildBuilderStep<'msg, 'childMarker>) =
let childAttr = this.Attr.WithValue(result.Invoke().Compile())
let struct (scalars, widgets, widgetCollections) = this.AttributesBundle

let struct (scalars, widgets, widgetCollections, environments) =
this.AttributesBundle

WidgetBuilder<'msg, 'marker>(
this.WidgetKey,
Expand All @@ -282,6 +324,7 @@ type SingleChildBuilder<'msg, 'marker, 'childMarker when 'msg: equality> =
(match widgets with
| ValueNone -> ValueSome [| childAttr |]
| ValueSome widgets -> ValueSome(Array.appendOne childAttr widgets)),
widgetCollections
widgetCollections,
environments
)
)
22 changes: 16 additions & 6 deletions src/Fabulous/Components/Component.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type Component

member private this.MergeAttributes(rootWidget: Widget, componentWidgetOpt: Widget voption) =
match componentWidgetOpt with
| ValueNone -> struct (rootWidget.ScalarAttributes, rootWidget.WidgetAttributes, rootWidget.WidgetCollectionAttributes)
| ValueNone ->
struct (rootWidget.ScalarAttributes, rootWidget.WidgetAttributes, rootWidget.WidgetCollectionAttributes, rootWidget.EnvironmentAttributes)

| ValueSome componentWidget ->
let componentScalars =
Expand Down Expand Up @@ -60,7 +61,14 @@ type Component
| ValueNone, ValueSome attrs -> ValueSome attrs
| ValueSome widgetAttrs, ValueSome componentAttrs -> ValueSome(Array.append componentAttrs widgetAttrs)

struct (scalars, widgets, widgetColls)
let environments =
match struct (rootWidget.EnvironmentAttributes, componentWidget.EnvironmentAttributes) with
| ValueNone, ValueNone -> ValueNone
| ValueSome attrs, ValueNone
| ValueNone, ValueSome attrs -> ValueSome attrs
| ValueSome widgetAttrs, ValueSome componentAttrs -> ValueSome(Array.append componentAttrs widgetAttrs)

struct (scalars, widgets, widgetColls, environments)

member this.CreateView(componentWidget: Widget voption) =
_isReadyForRenderRequest <- false
Expand All @@ -74,7 +82,7 @@ type Component
_treeContext <- treeContext
_context <- context

let struct (scalars, widgets, widgetColls) =
let struct (scalars, widgets, widgetColls, environments) =
this.MergeAttributes(rootWidget, componentWidget)

let rootWidget: Widget =
Expand All @@ -84,7 +92,8 @@ type Component
#endif
ScalarAttributes = scalars
WidgetAttributes = widgets
WidgetCollectionAttributes = widgetColls }
WidgetCollectionAttributes = widgetColls
EnvironmentAttributes = environments }

// Create the actual view
let widgetDef = WidgetDefinitionStore.get rootWidget.Key
Expand Down Expand Up @@ -115,7 +124,7 @@ type Component
_treeContext <- treeContext
_context <- context

let struct (scalars, widgets, widgetColls) =
let struct (scalars, widgets, widgetColls, environments) =
this.MergeAttributes(rootWidget, ValueSome componentWidget)

let rootWidget: Widget =
Expand All @@ -125,7 +134,8 @@ type Component
#endif
ScalarAttributes = scalars
WidgetAttributes = widgets
WidgetCollectionAttributes = widgetColls }
WidgetCollectionAttributes = widgetColls
EnvironmentAttributes = environments }

// Attach the widget to the existing view
let widgetDef = WidgetDefinitionStore.get rootWidget.Key
Expand Down
Loading

0 comments on commit 6fe5bbc

Please sign in to comment.