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

Add equality constraint on 'msg to prevent confusion with new non-MVU modifiers #1084

Merged
merged 5 commits into from
Oct 24, 2024
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
5 changes: 3 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"isRoot": true,
"tools": {
"fantomas": {
"version": "6.2.3",
"version": "6.3.15",
"commands": [
"fantomas"
]
],
"rollForward": false
}
}
}
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

_No unreleased changes_

## [3.0.0-pre6] - 2024-09-24

### Changed
- Add equality constraint on 'msg to prevent confusion with new non-MVU modifiers by @TimLariviere https://github.com/fabulous-dev/Fabulous/pull/1084

### Added
- Add new `CollectionBuilder` constructor using an `AttributesBundle` by @edgarfgp in https://github.com/fabulous-dev/Fabulous/pull/1081

## [3.0.0-pre5] - 2024-05-17

### Added
Expand Down Expand Up @@ -139,7 +147,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-pre5...HEAD
[unreleased]: https://github.com/fabulous-dev/Fabulous/compare/3.0.0-pre6...HEAD
[3.0.0-pre6]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre6
[3.0.0-pre5]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre5
[3.0.0-pre4]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre4
[3.0.0-pre3]: https://github.com/fabulous-dev/Fabulous/releases/tag/3.0.0-pre3
Expand Down
12 changes: 6 additions & 6 deletions src/Fabulous.Tests/APISketchTests/APISketchTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -642,29 +642,29 @@ module Issue104 =

let ControlWidgetKey = Widgets.register<TestButton>()

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

[<System.Runtime.CompilerServices.Extension>]
type WidgetExtensions() =
[<System.Runtime.CompilerServices.Extension>]
static member inline attr1<'msg, 'marker when 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline attr1<'msg, 'marker when 'msg: equality and 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attr1.WithValue(value))

[<System.Runtime.CompilerServices.Extension>]
static member inline attr2<'msg, 'marker when 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline attr2<'msg, 'marker when 'msg: equality and 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attr2.WithValue(value))

[<System.Runtime.CompilerServices.Extension>]
static member inline attr3<'msg, 'marker when 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline attr3<'msg, 'marker when 'msg: equality and 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attr3.WithValue(value))

[<System.Runtime.CompilerServices.Extension>]
static member inline attr4<'msg, 'marker when 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline attr4<'msg, 'marker when 'msg: equality and 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attr4.WithValue(value))

[<System.Runtime.CompilerServices.Extension>]
static member inline attr5<'msg, 'marker when 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline attr5<'msg, 'marker when 'msg: equality and 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attr5.WithValue(value))

let view model =
Expand Down
62 changes: 26 additions & 36 deletions src/Fabulous.Tests/APISketchTests/TestUI.Widgets.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ module TestUI_Widgets =


//-----MARKERS-----------
type IMarker =
interface
end
type IMarker = interface end

type TextMarker =
inherit IMarker
Expand All @@ -84,39 +82,37 @@ module TestUI_Widgets =
[<Extension>]
type WidgetExtensions() =
[<Extension>]
static member inline automationId<'msg, 'marker when 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline automationId<'msg, 'marker when 'msg: equality and 'marker :> IMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attributes.Automation.AutomationId.WithValue(value))

[<Extension>]
static member inline automationId<'msg, 'marker, 'itemMarker when 'marker :> IMarker>
(
this: CollectionBuilder<'msg, 'marker, 'itemMarker>,
value: string
) =
static member inline automationId<'msg, 'marker, 'itemMarker when 'msg: equality and 'marker :> IMarker>
(this: CollectionBuilder<'msg, 'marker, 'itemMarker>, value: string)
=
this.AddScalar(Attributes.Automation.AutomationId.WithValue(value))

[<Extension>]
static member inline textColor<'msg, 'marker when 'marker :> TextMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
static member inline textColor<'msg, 'marker when 'msg: equality and 'marker :> TextMarker>(this: WidgetBuilder<'msg, 'marker>, value: string) =
this.AddScalar(Attributes.TextStyle.TextColor.WithValue(value))


[<Extension>]
static member inline record<'msg, 'marker when 'marker :> TestLabelMarker>(this: WidgetBuilder<'msg, 'marker>, value: bool) =
static member inline record<'msg, 'marker when 'msg: equality and 'marker :> TestLabelMarker>(this: WidgetBuilder<'msg, 'marker>, value: bool) =
this.AddScalar(Attributes.Text.Record.WithValue(value))


[<Extension>]
static member inline tap<'msg, 'marker when 'marker :> TestButtonMarker>(this: WidgetBuilder<'msg, 'marker>, value: 'msg) =
static member inline tap<'msg, 'marker when 'msg: equality and 'marker :> TestButtonMarker>(this: WidgetBuilder<'msg, 'marker>, value: 'msg) =
this.AddScalar(Attributes.Button.Tap.WithValue(value))


[<Extension>]
static member inline tap2<'msg, 'marker when 'marker :> TestButtonMarker>(this: WidgetBuilder<'msg, 'marker>, value: 'msg) =
static member inline tap2<'msg, 'marker when 'msg: equality and 'marker :> TestButtonMarker>(this: WidgetBuilder<'msg, 'marker>, value: 'msg) =
this.AddScalar(Attributes.Button.Tap2.WithValue(value))


[<Extension>]
static member inline tapContainer<'msg, 'marker when 'marker :> TestStackMarker>(this: WidgetBuilder<'msg, 'marker>, value: 'msg) =
static member inline tapContainer<'msg, 'marker when 'msg: equality and 'marker :> TestStackMarker>(this: WidgetBuilder<'msg, 'marker>, value: 'msg) =
this.AddScalar(Attributes.Container.Tap.WithValue(value))

///----------------
Expand All @@ -129,22 +125,22 @@ module TestUI_Widgets =
static let TestStackKey = Widgets.register<TestStack>()
static let TestNumericBagKey = Widgets.register<TestNumericBag>()

static member Label<'msg>(text: string) =
static member Label(text: string) =
WidgetBuilder<'msg, TestLabelMarker>(TestLabelKey, Attributes.Text.Text.WithValue(text))


static member Button<'msg>(text: string, onClicked: 'msg) =
static member Button(text: string, onClicked: 'msg) =
WidgetBuilder<'msg, TestButtonMarker>(TestButtonKey, Attributes.Text.Text.WithValue(text), Attributes.Button.Pressed.WithValue(onClicked))

static member BoxedNumericBag<'msg>(one, two, three) =
static member BoxedNumericBag(one, two, three) =
WidgetBuilder<'msg, TestNumericBagMarker>(
TestNumericBagKey,
Attributes.NumericBag.BoxedValueOne.WithValue(one),
Attributes.NumericBag.BoxedValueTwo.WithValue(two),
Attributes.NumericBag.BoxedValueThree.WithValue(three)
)

static member InlineNumericBag<'msg>(one, two, three) =
static member InlineNumericBag(one, two, three) =
WidgetBuilder<'msg, TestNumericBagMarker>(
TestNumericBagKey,
Attributes.NumericBag.InlineValueOne.WithValue(one, (fun x -> x)),
Expand All @@ -154,41 +150,35 @@ module TestUI_Widgets =
Attributes.NumericBag.InlineValueThree.WithValue(three, BitConverter.DoubleToUInt64Bits)
)

static member Stack<'msg, 'marker when 'marker :> IMarker>() =
static member Stack<'msg, 'marker when 'msg: equality and 'marker :> IMarker>() =
CollectionBuilder<'msg, TestStackMarker, 'marker>(TestStackKey, StackList.empty(), Attributes.Container.Children)

[<Extension>]
type CollectionBuilderExtensions =
[<Extension>]
static member inline Yield<'msg, 'marker, 'itemMarker when 'itemMarker :> IMarker>
(
_: CollectionBuilder<'msg, 'marker, IMarker>,
x: WidgetBuilder<'msg, 'itemMarker>
) : Content<'msg> =
static member inline Yield<'msg, 'marker, 'itemMarker when 'msg: equality and 'itemMarker :> IMarker>
(_: CollectionBuilder<'msg, 'marker, IMarker>, x: WidgetBuilder<'msg, 'itemMarker>)
: Content<'msg> =
CollectionBuilder.yieldImpl x

[<Extension>]
static member inline Yield<'msg, 'marker, 'itemMarker when 'itemMarker :> IMarker>
(
_: CollectionBuilder<'msg, 'marker, IMarker>,
x: WidgetBuilder<'msg, Memo.Memoized<'itemMarker>>
) : Content<'msg> =
static member inline Yield<'msg, 'marker, 'itemMarker when 'msg: equality and 'itemMarker :> IMarker>
(_: CollectionBuilder<'msg, 'marker, IMarker>, x: WidgetBuilder<'msg, Memo.Memoized<'itemMarker>>)
: Content<'msg> =
CollectionBuilder.yieldImpl x

[<Extension>]
static member inline YieldFrom<'msg, 'marker, 'itemMarker when 'itemMarker :> IMarker>
(
_: CollectionBuilder<'msg, 'marker, IMarker>,
x: WidgetBuilder<'msg, 'itemMarker> seq
) : Content<'msg> =
static member inline YieldFrom<'msg, 'marker, 'itemMarker when 'msg: equality and 'itemMarker :> IMarker>
(_: CollectionBuilder<'msg, 'marker, IMarker>, x: WidgetBuilder<'msg, 'itemMarker> seq)
: Content<'msg> =
// TODO optimize this one with addMut
{ Widgets = x |> Seq.map(fun wb -> wb.Compile()) |> Seq.toArray |> MutStackArray1.fromArray }




///------------------
type StatefulView<'arg, 'model, 'msg, 'marker> =
type StatefulView<'arg, 'model, 'msg, 'marker when 'msg: equality> =
{ Init: 'arg -> 'model
Update: 'msg -> 'model -> 'model
View: 'model -> WidgetBuilder<'msg, 'marker> }
Expand All @@ -201,7 +191,7 @@ module TestUI_Widgets =


module Run =
type Instance<'arg, 'model, 'msg, 'marker>(program: StatefulView<'arg, 'model, 'msg, 'marker>) =
type Instance<'arg, 'model, 'msg, 'marker when 'msg: equality>(program: StatefulView<'arg, 'model, 'msg, 'marker>) =
let mutable state: ('model * obj * Widget) option = None

member private x.viewContext: ViewTreeContext =
Expand Down
4 changes: 1 addition & 3 deletions src/Fabulous.Tests/ViewTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ open Fabulous
open Fabulous.StackAllocatedCollections.StackList
open NUnit.Framework

type ITestControl =
interface
end
type ITestControl = interface end

module TestControl =
let WidgetKey: WidgetKey = 1
Expand Down
12 changes: 4 additions & 8 deletions src/Fabulous/AttributeDefinitions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ module ScalarAttributeDefinitions =
Value = null }

static member inline CreateAttributeData<'T>
(
[<InlineIfLambda>] decode: uint64 -> 'T,
[<InlineIfLambda>] updateNode: 'T voption -> 'T voption -> IViewNode -> unit
) : SmallScalarAttributeData =
([<InlineIfLambda>] decode: uint64 -> 'T, [<InlineIfLambda>] updateNode: 'T voption -> 'T voption -> IViewNode -> unit)
: SmallScalarAttributeData =
{ UpdateNode =
(fun oldValueOpt newValueOpt node ->
let oldValueOpt =
Expand Down Expand Up @@ -64,10 +62,8 @@ module ScalarAttributeDefinitions =
Value = value }

static member CreateAttributeData
(
compare: 'T -> 'T -> ScalarAttributeComparison,
updateNode: 'T voption -> 'T voption -> IViewNode -> unit
) : ScalarAttributeData =
(compare: 'T -> 'T -> ScalarAttributeComparison, updateNode: 'T voption -> 'T voption -> IViewNode -> unit)
: ScalarAttributeData =
{ CompareBoxed = (fun a b -> compare (unbox<'T> a) (unbox<'T> b))
UpdateNode =
(fun oldValueOpt newValueOpt node ->
Expand Down
6 changes: 2 additions & 4 deletions src/Fabulous/Attributes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@ type SmallScalarExtensions() =

[<Extension>]
static member inline WithValue< ^T when ^T: enum<int> and ^T: (static member op_Explicit: ^T -> uint64)>
(
this: SmallScalarAttributeDefinition< ^T >,
value
) =
(this: SmallScalarAttributeDefinition< ^T >, value)
=
this.WithValue(value, SmallScalars.IntEnum.encode)

type MsgValue = MsgValue of obj
Expand Down
16 changes: 7 additions & 9 deletions src/Fabulous/Builders.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ open Microsoft.FSharp.Core
type AttributesBundle = (struct (StackList<ScalarAttribute> * WidgetAttribute[] voption * WidgetCollectionAttribute[] voption))

[<Struct; NoComparison; NoEquality>]
type WidgetBuilder<'msg, 'marker> =
type WidgetBuilder<'msg, 'marker when 'msg: equality> =
struct
val Key: WidgetKey
val Attributes: AttributesBundle
Expand Down Expand Up @@ -122,7 +122,7 @@ type WidgetBuilder<'msg, 'marker> =
type Content<'msg> = { Widgets: MutStackArray1.T<Widget> }

[<Struct; NoComparison; NoEquality>]
type CollectionBuilder<'msg, 'marker, 'itemMarker> =
type CollectionBuilder<'msg, 'marker, 'itemMarker when 'msg: equality> =
struct
val WidgetKey: WidgetKey
val Attr: WidgetCollectionAttributeDefinition
Expand Down Expand Up @@ -207,7 +207,7 @@ module CollectionBuilder =
{ Widgets = MutStackArray1.One(builder.Compile()) }

[<Struct>]
type AttributeCollectionBuilder<'msg, 'marker, 'itemMarker> =
type AttributeCollectionBuilder<'msg, 'marker, 'itemMarker when 'msg: equality> =
struct
val Widget: WidgetBuilder<'msg, 'marker>
val Attr: WidgetCollectionAttributeDefinition
Expand Down Expand Up @@ -240,10 +240,10 @@ type AttributeCollectionBuilder<'msg, 'marker, 'itemMarker> =
res
end

type SingleChildBuilderStep<'msg, 'marker> = delegate of unit -> WidgetBuilder<'msg, 'marker>
type SingleChildBuilderStep<'msg, 'marker when 'msg: equality> = delegate of unit -> WidgetBuilder<'msg, 'marker>

[<Struct>]
type SingleChildBuilder<'msg, 'marker, 'childMarker> =
type SingleChildBuilder<'msg, 'marker, 'childMarker when 'msg: equality> =
val WidgetKey: WidgetKey
val Attr: WidgetAttributeDefinition
val AttributesBundle: AttributesBundle
Expand All @@ -262,10 +262,8 @@ type SingleChildBuilder<'msg, 'marker, 'childMarker> =
SingleChildBuilderStep(fun () -> widget)

member inline this.Combine
(
[<InlineIfLambda>] a: SingleChildBuilderStep<'msg, 'childMarker>,
[<InlineIfLambda>] _b: SingleChildBuilderStep<'msg, 'childMarker>
) =
([<InlineIfLambda>] a: SingleChildBuilderStep<'msg, 'childMarker>, [<InlineIfLambda>] _b: SingleChildBuilderStep<'msg, 'childMarker>)
=
SingleChildBuilderStep(fun () ->
// We only want one child, so we ignore the second one
a.Invoke())
Expand Down
Loading
Loading