Skip to content

Commit e45bdf4

Browse files
committed
Support for multiple environment values on single widget
1 parent b2c5cdd commit e45bdf4

File tree

12 files changed

+286
-68
lines changed

12 files changed

+286
-68
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
_No unreleased changes_
1111

12+
## [3.0.0-pre12] - 2024-11-20
13+
14+
### Added
15+
- Add support for multiple environment modifiers on the same widget by @TimLariviere
16+
1217
## [3.0.0-pre11] - 2024-11-16
1318

1419
### Changed
@@ -172,7 +177,8 @@ _No unreleased changes_
172177
### Changed
173178
- 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)
174179

175-
[unreleased]: https://github.com/fabulous-dev/Fabulous/compare/3.0.0-pre11...HEAD
180+
[unreleased]: https://github.com/fabulous-dev/Fabulous/compare/3.0.0-pre12...HEAD
181+
[3.0.0-pre12]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre12
176182
[3.0.0-pre11]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre11
177183
[3.0.0-pre10]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre10
178184
[3.0.0-pre9]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre9

global.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"sdk": {
3+
"version": "8.0.404"
4+
}
5+
}

src/Fabulous.Tests/APISketchTests/APISketchTests.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ module Issue104 =
643643
let ControlWidgetKey = Widgets.register<TestButton>()
644644

645645
let Control () =
646-
WidgetBuilder<'msg, TestButtonMarker>(ControlWidgetKey, AttributesBundle(StackList.StackList.empty(), ValueNone, ValueNone))
646+
WidgetBuilder<'msg, TestButtonMarker>(ControlWidgetKey)
647647

648648
[<System.Runtime.CompilerServices.Extension>]
649649
type WidgetExtensions() =

src/Fabulous.Tests/ViewTests.fs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,15 @@ type ``View helper functions tests``() =
1717
/// Test: https://github.com/fabulous-dev/Fabulous/pull/1037
1818
[<Test>]
1919
member _.``Mapping a WidgetBuilder with Unit Msg to another Msg is supported``() =
20-
let widgetBuilder =
21-
WidgetBuilder<unit, ITestControl>(TestControl.WidgetKey, AttributesBundle(StackList.empty(), ValueNone, ValueNone))
20+
let widgetBuilder = WidgetBuilder<unit, ITestControl>(TestControl.WidgetKey)
2221

2322
let mapMsg (oldMsg: unit) = ChildMsg oldMsg
2423

2524
let mappedWidgetBuilder = View.map mapMsg widgetBuilder
2625

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

29-
let struct (scalars, _, _) = mappedWidgetBuilder.Attributes
28+
let struct (scalars, _, _, _) = mappedWidgetBuilder.Attributes
3029
let scalars = StackList.toArray &scalars
3130

3231
Assert.AreEqual(1, scalars.Length)

src/Fabulous/AttributeDefinitions.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ module AttributeDefinitionStore =
160160
let private _scalars = ResizeArray<ScalarAttributeData>()
161161
let private _smallScalars = ResizeArray<SmallScalarAttributeData>()
162162
let private _widgets = ResizeArray<WidgetAttributeData>()
163-
164163
let private _widgetCollections = ResizeArray<WidgetCollectionAttributeData>()
165164

166165
let registerSmallScalar (data: SmallScalarAttributeData) : ScalarAttributeKey =

src/Fabulous/Builders.fs

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,39 @@ open Fabulous.StackAllocatedCollections
77
open Fabulous.StackAllocatedCollections.StackList
88
open Microsoft.FSharp.Core
99

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

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

18+
new(key: WidgetKey) =
19+
{ Key = key
20+
Attributes = AttributesBundle(StackList.empty(), ValueNone, ValueNone, ValueNone) }
21+
1822
new(key: WidgetKey, attributes: AttributesBundle) = { Key = key; Attributes = attributes }
1923

2024
new(key: WidgetKey, scalar: ScalarAttribute) =
2125
{ Key = key
22-
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone) }
26+
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone, ValueNone) }
2327

2428
new(key: WidgetKey, scalarA: ScalarAttribute, scalarB: ScalarAttribute) =
2529
{ Key = key
26-
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone) }
30+
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone, ValueNone) }
2731

2832
new(key: WidgetKey, scalar1: ScalarAttribute, scalar2: ScalarAttribute, scalar3: ScalarAttribute) =
2933
{ Key = key
30-
Attributes = AttributesBundle(StackList.three(scalar1, scalar2, scalar3), ValueNone, ValueNone) }
34+
Attributes = AttributesBundle(StackList.three(scalar1, scalar2, scalar3), ValueNone, ValueNone, ValueNone) }
35+
36+
new(key: WidgetKey, widget: WidgetAttribute) =
37+
{ Key = key
38+
Attributes = AttributesBundle(StackList.empty(), ValueSome [| widget |], ValueNone, ValueNone) }
3139

3240
[<EditorBrowsable(EditorBrowsableState.Never)>]
3341
member x.Compile() : Widget =
34-
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
42+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
3543
x.Attributes
3644

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

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

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

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

5160
[<EditorBrowsable(EditorBrowsableState.Never)>]
5261
member inline x.AddScalar(attr: ScalarAttribute) =
53-
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
62+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
5463
x.Attributes
5564

56-
WidgetBuilder<'msg, 'marker>(x.Key, struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes))
65+
WidgetBuilder<'msg, 'marker>(
66+
x.Key,
67+
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes, environmentAttributes)
68+
)
5769

5870
[<EditorBrowsable(EditorBrowsableState.Never)>]
5971
member inline x.AddOrReplaceScalar
@@ -62,26 +74,29 @@ type WidgetBuilder<'msg, 'marker when 'msg: equality> =
6274
[<InlineIfLambda>] replaceWith: ScalarAttribute -> ScalarAttribute,
6375
[<InlineIfLambda>] defaultWith: unit -> ScalarAttribute
6476
) =
65-
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
77+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
6678
x.Attributes
6779

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

72-
WidgetBuilder<'msg, 'marker>(x.Key, struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes))
84+
WidgetBuilder<'msg, 'marker>(
85+
x.Key,
86+
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes, environmentAttributes)
87+
)
7388

7489
| ValueSome attr ->
7590
let newAttr = replaceWith attr
7691

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

80-
WidgetBuilder<'msg, 'marker>(x.Key, struct (newAttrs, widgetAttributes, widgetCollectionAttributes))
95+
WidgetBuilder<'msg, 'marker>(x.Key, struct (newAttrs, widgetAttributes, widgetCollectionAttributes, environmentAttributes))
8196

8297
[<EditorBrowsable(EditorBrowsableState.Never)>]
8398
member x.AddWidget(attr: WidgetAttribute) =
84-
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
99+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
85100
x.Attributes
86101

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

98-
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, ValueSome res, widgetCollectionAttributes))
113+
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, ValueSome res, widgetCollectionAttributes, environmentAttributes))
99114

100115
[<EditorBrowsable(EditorBrowsableState.Never)>]
101116
member x.AddWidgetCollection(attr: WidgetCollectionAttribute) =
102-
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
117+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
103118
x.Attributes
104119

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

116-
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, widgetAttributes, ValueSome res))
131+
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, widgetAttributes, ValueSome res, environmentAttributes))
132+
133+
[<EditorBrowsable(EditorBrowsableState.Never)>]
134+
member inline x.AddEnvironment(key: EnvironmentAttributeKey, value: obj) =
135+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
136+
x.Attributes
137+
138+
let attr =
139+
{ Key = key
140+
#if DEBUG
141+
DebugName = $"Environment.{key}"
142+
#endif
143+
Value = value }
144+
145+
let attribs = environmentAttributes
146+
147+
let res =
148+
match attribs with
149+
| ValueNone -> [| attr |]
150+
| ValueSome attribs ->
151+
let attribs2 = Array.zeroCreate(attribs.Length + 1)
152+
Array.blit attribs 0 attribs2 0 attribs.Length
153+
attribs2[attribs.Length] <- attr
154+
attribs2
155+
156+
WidgetBuilder<'msg, 'marker>(x.Key, struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, ValueSome res))
117157
end
118158

119159

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

131171
new(widgetKey: WidgetKey, scalars: StackList<ScalarAttribute>, attr: WidgetCollectionAttributeDefinition) =
132172
{ WidgetKey = widgetKey
133-
Attributes = AttributesBundle(scalars, ValueNone, ValueNone)
173+
Attributes = AttributesBundle(scalars, ValueNone, ValueNone, ValueNone)
134174
Attr = attr }
135175

136176
new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition) =
137177
{ WidgetKey = widgetKey
138-
Attributes = AttributesBundle(StackList.empty(), ValueNone, ValueNone)
178+
Attributes = AttributesBundle(StackList.empty(), ValueNone, ValueNone, ValueNone)
139179
Attr = attr }
140180

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

146186
new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition, scalar: ScalarAttribute) =
147187
{ WidgetKey = widgetKey
148-
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone)
188+
Attributes = AttributesBundle(StackList.one scalar, ValueNone, ValueNone, ValueNone)
149189
Attr = attr }
150190

151191
new(widgetKey: WidgetKey, attr: WidgetCollectionAttributeDefinition, scalarA: ScalarAttribute, scalarB: ScalarAttribute) =
152192
{ WidgetKey = widgetKey
153-
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone)
193+
Attributes = AttributesBundle(StackList.two(scalarA, scalarB), ValueNone, ValueNone, ValueNone)
154194
Attr = attr }
155195

156196
member inline x.Run(c: Content<'msg>) =
157-
let struct (scalars, widgets, widgetCollections) = x.Attributes
197+
let struct (scalars, widgets, widgetCollections, environments) = x.Attributes
158198

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

171-
WidgetBuilder<'msg, 'marker>(x.WidgetKey, AttributesBundle(scalars, widgets, widgetCollections))
211+
WidgetBuilder<'msg, 'marker>(x.WidgetKey, AttributesBundle(scalars, widgets, widgetCollections, environments))
172212

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

192232
[<EditorBrowsable(EditorBrowsableState.Never)>]
193233
member inline x.AddScalar(attr: ScalarAttribute) =
194-
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes) =
234+
let struct (scalarAttributes, widgetAttributes, widgetCollectionAttributes, environmentAttributes) =
195235
x.Attributes
196236

197237
CollectionBuilder<'msg, 'marker, 'itemMarker>(
198238
x.WidgetKey,
199239
x.Attr,
200-
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes)
240+
struct (StackList.add(&scalarAttributes, attr), widgetAttributes, widgetCollectionAttributes, environmentAttributes)
201241
)
202242

203243
end
@@ -251,7 +291,7 @@ type SingleChildBuilder<'msg, 'marker, 'childMarker when 'msg: equality> =
251291
new(widgetKey: WidgetKey, attr: WidgetAttributeDefinition) =
252292
{ WidgetKey = widgetKey
253293
Attr = attr
254-
AttributesBundle = AttributesBundle(StackList.empty(), ValueNone, ValueNone) }
294+
AttributesBundle = AttributesBundle(StackList.empty(), ValueNone, ValueNone, ValueNone) }
255295

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

274314
member inline this.Run([<InlineIfLambda>] result: SingleChildBuilderStep<'msg, 'childMarker>) =
275315
let childAttr = this.Attr.WithValue(result.Invoke().Compile())
276-
let struct (scalars, widgets, widgetCollections) = this.AttributesBundle
316+
317+
let struct (scalars, widgets, widgetCollections, environments) =
318+
this.AttributesBundle
277319

278320
WidgetBuilder<'msg, 'marker>(
279321
this.WidgetKey,
@@ -282,6 +324,7 @@ type SingleChildBuilder<'msg, 'marker, 'childMarker when 'msg: equality> =
282324
(match widgets with
283325
| ValueNone -> ValueSome [| childAttr |]
284326
| ValueSome widgets -> ValueSome(Array.appendOne childAttr widgets)),
285-
widgetCollections
327+
widgetCollections,
328+
environments
286329
)
287330
)

src/Fabulous/Components/Component.fs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ type Component
2727

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

3233
| ValueSome componentWidget ->
3334
let componentScalars =
@@ -60,7 +61,14 @@ type Component
6061
| ValueNone, ValueSome attrs -> ValueSome attrs
6162
| ValueSome widgetAttrs, ValueSome componentAttrs -> ValueSome(Array.append componentAttrs widgetAttrs)
6263

63-
struct (scalars, widgets, widgetColls)
64+
let environments =
65+
match struct (rootWidget.EnvironmentAttributes, componentWidget.EnvironmentAttributes) with
66+
| ValueNone, ValueNone -> ValueNone
67+
| ValueSome attrs, ValueNone
68+
| ValueNone, ValueSome attrs -> ValueSome attrs
69+
| ValueSome widgetAttrs, ValueSome componentAttrs -> ValueSome(Array.append componentAttrs widgetAttrs)
70+
71+
struct (scalars, widgets, widgetColls, environments)
6472

6573
member this.CreateView(componentWidget: Widget voption) =
6674
_isReadyForRenderRequest <- false
@@ -74,7 +82,7 @@ type Component
7482
_treeContext <- treeContext
7583
_context <- context
7684

77-
let struct (scalars, widgets, widgetColls) =
85+
let struct (scalars, widgets, widgetColls, environments) =
7886
this.MergeAttributes(rootWidget, componentWidget)
7987

8088
let rootWidget: Widget =
@@ -84,7 +92,8 @@ type Component
8492
#endif
8593
ScalarAttributes = scalars
8694
WidgetAttributes = widgets
87-
WidgetCollectionAttributes = widgetColls }
95+
WidgetCollectionAttributes = widgetColls
96+
EnvironmentAttributes = environments }
8897

8998
// Create the actual view
9099
let widgetDef = WidgetDefinitionStore.get rootWidget.Key
@@ -115,7 +124,7 @@ type Component
115124
_treeContext <- treeContext
116125
_context <- context
117126

118-
let struct (scalars, widgets, widgetColls) =
127+
let struct (scalars, widgets, widgetColls, environments) =
119128
this.MergeAttributes(rootWidget, ValueSome componentWidget)
120129

121130
let rootWidget: Widget =
@@ -125,7 +134,8 @@ type Component
125134
#endif
126135
ScalarAttributes = scalars
127136
WidgetAttributes = widgets
128-
WidgetCollectionAttributes = widgetColls }
137+
WidgetCollectionAttributes = widgetColls
138+
EnvironmentAttributes = environments }
129139

130140
// Attach the widget to the existing view
131141
let widgetDef = WidgetDefinitionStore.get rootWidget.Key

0 commit comments

Comments
 (0)