diff --git a/CHANGELOG.md b/CHANGELOG.md index c037e3bc4..41102f73e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 _No unreleased changes_ +## [2.5.0-pre4] - 2024-01-18 + +### Changed +- Couple of changes and fixes to the new Component API by @TimLariviere (https://github.com/fabulous-dev/Fabulous/pull/1057) + ## [2.5.0-pre3] - 2024-01-16 ### Changed @@ -65,7 +70,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/2.5.0-pre3...HEAD +[unreleased]: https://github.com/fabulous-dev/Fabulous/compare/2.5.0-pre4...HEAD +[2.5.0-pre4]: https://github.com/fabulous-dev/Fabulous/releases/tag/2.5.0-pre4 [2.5.0-pre3]: https://github.com/fabulous-dev/Fabulous/releases/tag/2.5.0-pre3 [2.5.0-pre2]: https://github.com/fabulous-dev/Fabulous/releases/tag/2.5.0-pre2 [2.5.0-pre1]: https://github.com/fabulous-dev/Fabulous/releases/tag/2.5.0-pre1 diff --git a/src/Fabulous/Component.fs b/src/Fabulous/Component.fs index e6af90140..3780bb7d0 100644 --- a/src/Fabulous/Component.fs +++ b/src/Fabulous/Component.fs @@ -333,7 +333,7 @@ module Component = /// It will be aggressively inlined by the compiler leaving no overhead, only a pure function that returns a WidgetBuilder type ComponentBodyBuilder<'marker> = delegate of bindings: int * context: ComponentContext -> struct (int * WidgetBuilder) -type ComponentBuilder() = +type ComponentBuilder<'parentMsg>() = member inline this.Yield(widgetBuilder: WidgetBuilder) = ComponentBodyBuilder<'marker>(fun bindings ctx -> struct (bindings, widgetBuilder)) @@ -360,4 +360,4 @@ type ComponentBuilder() = let data = { Body = compiledBody } - WidgetBuilder(Component.WidgetKey, Component.Data.WithValue(data)) + WidgetBuilder<'parentMsg, 'marker>(Component.WidgetKey, Component.Data.WithValue(data)) diff --git a/src/Fabulous/ComponentContext.fs b/src/Fabulous/ComponentContext.fs index 9789baed9..ca2dc05e3 100644 --- a/src/Fabulous/ComponentContext.fs +++ b/src/Fabulous/ComponentContext.fs @@ -19,6 +19,13 @@ we can leverage the inlining capabilities of the ComponentBuilder to create an a /// Holds the values for the various states of a component. /// type ComponentContext(initialSize: int) = + static let mutable nextId = 0 + + static let getNextId () = + nextId <- nextId + 1 + nextId + + let id = getNextId() let mutable values = Array.zeroCreate initialSize let renderNeeded = Event() @@ -26,6 +33,8 @@ type ComponentContext(initialSize: int) = // We assume that most components will have few values, so initialize it with a small array new() = ComponentContext(3) + member this.Id = id + member this.RenderNeeded = renderNeeded.Publish member this.NeedsRender() = renderNeeded.Trigger() diff --git a/src/Fabulous/MvuComponent.fs b/src/Fabulous/MvuComponent.fs index 62e8759d2..e534649bd 100644 --- a/src/Fabulous/MvuComponent.fs +++ b/src/Fabulous/MvuComponent.fs @@ -58,7 +58,7 @@ type MvuComponentBodyBuilder<'msg, 'marker> = delegate of bindings: int * context: ComponentContext -> struct (int * WidgetBuilder<'msg, 'marker>) [] -type MvuComponentBuilder<'arg, 'msg, 'model, 'marker> = +type MvuComponentBuilder<'arg, 'msg, 'model, 'marker, 'parentMsg> = val public Program: Program val public Arg: obj @@ -101,7 +101,7 @@ type MvuComponentBuilder<'arg, 'msg, 'model, 'marker> = Arg = this.Arg Body = compiledBody } - WidgetBuilder(MvuComponent.WidgetKey, MvuComponent.Data.WithValue(data)) + WidgetBuilder<'parentMsg, 'marker>(MvuComponent.WidgetKey, MvuComponent.Data.WithValue(data)) type MvuStateRequest = struct @@ -115,11 +115,16 @@ type MvuContextExtensions = [] static member inline Bind ( - _: MvuComponentBuilder<'arg, 'msg, 'model, 'marker>, + _: MvuComponentBuilder<'arg, 'msg, 'model, 'marker, 'parentMsg>, _: MvuStateRequest, [] continuation: 'model -> MvuComponentBodyBuilder<'msg, 'marker> ) = MvuComponentBodyBuilder<'msg, 'marker>(fun bindings ctx -> let key = int bindings - let value = ctx.TryGetValue<'model>(key).Value + + let value = + match ctx.TryGetValue<'model>(key) with + | ValueSome value -> value + | ValueNone -> failwith $"[MvuComponent.Bind] Model not found in ComponentContext {ctx.Id}" + (continuation value).Invoke((bindings + 1, ctx))) diff --git a/src/Fabulous/Program.fs b/src/Fabulous/Program.fs index e96da5043..069314b37 100644 --- a/src/Fabulous/Program.fs +++ b/src/Fabulous/Program.fs @@ -21,7 +21,7 @@ type Program<'arg, 'model, 'msg> = type Program<'arg, 'model, 'msg, 'marker> = { - Program: Program<'arg, 'model, 'msg> + State: Program<'arg, 'model, 'msg> /// Render the application state View: 'model -> WidgetBuilder<'msg, 'marker> /// Indicates if a previous Widget's view can be reused diff --git a/src/Fabulous/State.fs b/src/Fabulous/State.fs index 60dfd3fd8..c1d7a15a2 100644 --- a/src/Fabulous/State.fs +++ b/src/Fabulous/State.fs @@ -76,7 +76,7 @@ type StateExtensions = [] static member inline Bind ( - _: ComponentBuilder, + _: ComponentBuilder<'parentMsg>, [] fn: StateRequest<'T>, [] continuation: StateValue<'T> -> ComponentBodyBuilder<'marker> ) =