Skip to content

Commit

Permalink
Format code
Browse files Browse the repository at this point in the history
  • Loading branch information
TimLariviere committed Nov 25, 2023
1 parent 60c4092 commit dd2e4d0
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 117 deletions.
32 changes: 18 additions & 14 deletions src/Fabulous/Builders.fs
Original file line number Diff line number Diff line change
Expand Up @@ -218,47 +218,51 @@ type AttributeCollectionBuilder<'msg, 'marker, 'itemMarker> =

res
end

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

[<Struct>]
type SingleChildBuilder<'msg, 'marker, 'childMarker> =
val WidgetKey: WidgetKey
val Attr: WidgetAttributeDefinition
val AttributesBundle: AttributesBundle
new (widgetKey: WidgetKey, attr: WidgetAttributeDefinition) =

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

new(widgetKey: WidgetKey, attr: WidgetAttributeDefinition, attributesBundle: AttributesBundle) =
{ WidgetKey = widgetKey
Attr = attr
AttributesBundle = attributesBundle }

member inline this.Yield(widget: WidgetBuilder<'msg, 'childMarker>) =
SingleChildBuilderStep(fun () -> widget)

member inline this.Combine([<InlineIfLambda>] a: SingleChildBuilderStep<'msg, 'childMarker>, [<InlineIfLambda>] _b: SingleChildBuilderStep<'msg, 'childMarker>) =
member inline this.Combine
(
[<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()
)

a.Invoke())

member inline this.Delay([<InlineIfLambda>] fn: unit -> SingleChildBuilderStep<'msg, 'childMarker>) =
SingleChildBuilderStep(fun () -> fn().Invoke())

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

WidgetBuilder<'msg, 'marker>(
this.WidgetKey,
AttributesBundle(
scalars,
(match widgets with
| ValueNone -> ValueSome [| childAttr |]
| ValueSome widgets -> ValueSome(Array.appendOne childAttr widgets)),
| ValueNone -> ValueSome [| childAttr |]
| ValueSome widgets -> ValueSome(Array.appendOne childAttr widgets)),
widgetCollections
)
)
)
8 changes: 2 additions & 6 deletions src/Fabulous/Component/Builder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ namespace Fabulous

/// Delegate used by the ComponentBuilder to compose a component body
/// 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<binding> * context: ComponentContext -> struct (int<binding> * WidgetBuilder<unit, 'marker>)
type ComponentBodyBuilder<'marker> = delegate of bindings: int<binding> * context: ComponentContext -> struct (int<binding> * WidgetBuilder<unit, 'marker>)

type ComponentBuilder() =
member inline this.Yield(widgetBuilder: WidgetBuilder<unit, 'marker>) =
Expand All @@ -30,7 +29,4 @@ type ComponentBuilder() =
let struct (_, result) = body.Invoke(0<binding>, ctx)
struct (ctx, result.Compile()))

WidgetBuilder<unit, 'marker>(
ComponentWidget.WidgetKey,
Component.Body.WithValue(compiledBody)
)
WidgetBuilder<unit, 'marker>(ComponentWidget.WidgetKey, Component.Body.WithValue(compiledBody))
16 changes: 9 additions & 7 deletions src/Fabulous/Component/Component.fs
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,14 @@ avatar1.Background <- Blue

type ComponentBody = delegate of ComponentContext -> struct (ComponentContext * Widget)

type IBaseComponent = inherit IDisposable
type IBaseComponent =
inherit IDisposable

type IComponent =
inherit IBaseComponent
abstract member SetBody: ComponentBody -> unit
abstract member SetContext: ComponentContext -> unit

module Component =
// TODO: This is a big code smell. We should not do this but I can't think of a better way to do it right now.
// The implementation of this method is set by the consuming project: Fabulous.XamarinForms, Fabulous.Maui, Fabulous.Avalonia
Expand All @@ -221,10 +222,10 @@ module Component =
let mutable getAttachedComponent: obj -> IBaseComponent =
fun _ -> failwith "Please call Component.SetComponentFunctions() before using Component"

let setComponentFunctions(get: obj -> IBaseComponent, set: obj -> IBaseComponent -> unit) =
let setComponentFunctions (get: obj -> IBaseComponent, set: obj -> IBaseComponent -> unit) =
getAttachedComponent <- get
setAttachedComponent <- set

/// TODO: This is actually broken. On every call of the parent, the body will be reassigned to the Component triggering a re-render because of the noCompare.
/// This is not what was expected. The body should actually be invalidated based on its context.
let Body =
Expand All @@ -249,7 +250,7 @@ type Component(treeContext: ViewTreeContext, body: ComponentBody, context: Compo
let mutable _widget = Unchecked.defaultof<_>
let mutable _view = null
let mutable _contextSubscription: IDisposable = null

interface IComponent with
member this.SetBody(body: ComponentBody) =
_body <- body
Expand All @@ -260,7 +261,7 @@ type Component(treeContext: ViewTreeContext, body: ComponentBody, context: Compo
_contextSubscription <- context.RenderNeeded.Subscribe(this.Render)
_context <- context
this.Render()

member this.Dispose() =
if _contextSubscription <> null then
_contextSubscription.Dispose()
Expand All @@ -275,7 +276,8 @@ type Component(treeContext: ViewTreeContext, body: ComponentBody, context: Compo
let scalars =
match componentWidget.ScalarAttributes with
| ValueNone -> ValueNone
| ValueSome attrs -> ValueSome(Array.filter (fun (attr: ScalarAttribute) -> attr.Key <> Component.Body.Key && attr.Key <> Component.Context.Key) attrs)
| ValueSome attrs ->
ValueSome(Array.filter (fun (attr: ScalarAttribute) -> attr.Key <> Component.Body.Key && attr.Key <> Component.Context.Key) attrs)

let rootWidget: Widget =
{ Key = rootWidget.Key
Expand Down
6 changes: 3 additions & 3 deletions src/Fabulous/Component/Context.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ type ComponentContext(initialSize: int) =
let mutable values = Array.zeroCreate initialSize

let renderNeeded = Event<unit>()

// We assume that most components will have few values, so initialize it with a small array
new () = ComponentContext(3)
new() = ComponentContext(3)

member this.RenderNeeded = renderNeeded.Publish
member this.NeedsRender() = renderNeeded.Trigger()

Expand Down
142 changes: 71 additions & 71 deletions src/Fabulous/Component/MvuComponent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ open Fabulous.ScalarAttributeDefinitions

type Init<'msg, 'model> = unit -> 'model * Cmd<'msg>
type Update<'msg, 'model> = 'msg -> 'model -> 'model * Cmd<'msg>

// This MvuComponent is a proxy widget just like Component
// but its specificity is to override the ViewTreeContext.Dispatch
// with its own mvu.Dispatch to allow implicit dispatching in its children just like with Fabulous 2 DSL
Expand All @@ -25,102 +25,107 @@ type IMvuComponent =
type MvuComponent(treeContext: ViewTreeContext, data: MvuComponentData, context: ComponentContext) as this =
let mutable _body = data.Body
let mutable _arg = data.Arg
let mutable _runner = Runner<obj, obj, obj>(0, this.GetModel, this.SetModel, data.Program)

let mutable _runner =
Runner<obj, obj, obj>(0, this.GetModel, this.SetModel, data.Program)

let mutable _context = context
let mutable _widget = Unchecked.defaultof<Widget>
let mutable _view = null
let mutable _contextSubscription = null

interface IMvuComponent with
member this.SetData(data: MvuComponentData) =
_body <- data.Body
_arg <- data.Arg
// TODO: We should probably reset the runner here
this.Render()

member this.Dispose() =
if _contextSubscription <> null then
_contextSubscription.Dispose()
_contextSubscription <- null

member private this.GetModel(key: StateKey) =
match _context.TryGetValue(key) with
| ValueSome model -> model
| ValueNone ->
let initialModel, cmd = _runner.Program.Init _arg
_context.SetValueInternal(0, initialModel)

for sub in cmd do
_runner.Dispatch(sub)

initialModel

member private this.SetModel (key: StateKey) (model: obj) =
_context.SetValue(key, model)

member this.Dispatch(msg: obj) =
_runner.Dispatch(msg)

member this.CreateView() =

member private this.SetModel (key: StateKey) (model: obj) = _context.SetValue(key, model)

member this.Dispatch(msg: obj) = _runner.Dispatch(msg)

member this.CreateView() =
let widget = _body.Invoke(this.GetModel(0))
_widget <- widget

// Replace the global dispatch with the one from the MvuComponent
// so the child widgets can dispatch implicitly
let treeContext = { treeContext with Dispatch = this.Dispatch }

let treeContext =
{ treeContext with
Dispatch = this.Dispatch }

// Create the actual view
let widgetDef = WidgetDefinitionStore.get widget.Key
let struct (node, view) = widgetDef.CreateView(widget, treeContext, ValueNone)
_view <- view

_contextSubscription <- _context.RenderNeeded.Subscribe(this.Render)

struct (node, view)
member this.Render() =

member this.Render() =
let prevWidget = _widget
let widget = _body.Invoke(this.GetModel(0))
_widget <- widget

let viewNode = treeContext.GetViewNode _view

Reconciler.update treeContext.CanReuseView (ValueSome prevWidget) widget viewNode


module MvuComponent =
let Data: SimpleScalarAttributeDefinition<MvuComponentData> = Attributes.defineSimpleScalar "MvuComponent_Data" ScalarAttributeComparers.noCompare (fun _ currOpt node ->
let comp = Component.getAttachedComponent(node.Target) :?> IMvuComponent
match currOpt with
| ValueNone -> failwith "MvuComponent widget must have an associated MvuComponentData"
| ValueSome data -> comp.SetData data
)

let Data: SimpleScalarAttributeDefinition<MvuComponentData> =
Attributes.defineSimpleScalar "MvuComponent_Data" ScalarAttributeComparers.noCompare (fun _ currOpt node ->
let comp = Component.getAttachedComponent(node.Target) :?> IMvuComponent

match currOpt with
| ValueNone -> failwith "MvuComponent widget must have an associated MvuComponentData"
| ValueSome data -> comp.SetData data)

let WidgetKey =
let key = WidgetDefinitionStore.getNextKey()

let definition =
{ Key = key
Name = "MvuContext"
TargetType = typeof<MvuComponent>
AttachView = fun _ -> failwith "MvuComponent widget cannot be attached"
CreateView = (fun (widget, treeContext, _parentNode) ->
let data =
match widget.ScalarAttributes with
| ValueNone -> failwith "MvuComponent widget must have an associated MvuComponentData"
| ValueSome attrs ->
match Array.tryFind (fun (attr: ScalarAttribute) -> attr.Key = Data.Key) attrs with
| None -> failwith "MvuComponent widget must have an associated MvuComponentData"
| Some attr -> attr.Value :?> MvuComponentData

let comp = new MvuComponent(treeContext, data, ComponentContext(1))
let struct (node, view) = comp.CreateView()
struct (node, view)) }

CreateView =
(fun (widget, treeContext, _parentNode) ->
let data =
match widget.ScalarAttributes with
| ValueNone -> failwith "MvuComponent widget must have an associated MvuComponentData"
| ValueSome attrs ->
match Array.tryFind (fun (attr: ScalarAttribute) -> attr.Key = Data.Key) attrs with
| None -> failwith "MvuComponent widget must have an associated MvuComponentData"
| Some attr -> attr.Value :?> MvuComponentData

let comp = new MvuComponent(treeContext, data, ComponentContext(1))
let struct (node, view) = comp.CreateView()
struct (node, view)) }

WidgetDefinitionStore.set key definition

key

[<Struct>]
type MvuStateRequest = | MvuStateRequest

Expand All @@ -134,47 +139,42 @@ type Mvu =
type MvuComponentBuilder<'arg, 'msg, 'model, 'marker> =
val public Program: Program<obj, obj, obj>
val public Arg: obj
new (program: Program<'arg, 'model, 'msg>, arg: 'arg) =

new(program: Program<'arg, 'model, 'msg>, arg: 'arg) =
let program: Program<obj, obj, obj> =
{ Init = fun arg -> let model, cmd = program.Init(unbox arg) in (box model, Cmd.map box cmd)
Update = fun(msg, model) -> let model, cmd = program.Update(unbox msg, unbox model) in (box model, Cmd.map box cmd)
Update = fun (msg, model) -> let model, cmd = program.Update(unbox msg, unbox model) in (box model, Cmd.map box cmd)
Subscribe = fun model -> Cmd.map box (program.Subscribe(unbox model))
Logger = program.Logger
ExceptionHandler = program.ExceptionHandler }

{ Program = program
Arg = arg }


{ Program = program; Arg = arg }

member inline this.Bind(_value: MvuStateRequest, [<InlineIfLambda>] continuation: 'model -> MvuComponentBodyStep<'msg, 'model, 'marker>) =
MvuComponentBodyStep(fun model ->
(continuation model).Invoke(model)
)

MvuComponentBodyStep(fun model -> (continuation model).Invoke(model))

member inline this.Yield(widget: WidgetBuilder<'msg, 'marker>) =
MvuComponentBodyStep(fun model -> widget)

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

a.Invoke(model))

member inline this.Delay([<InlineIfLambda>] fn: unit -> MvuComponentBodyStep<'msg, 'model, 'marker>) =
MvuComponentBodyStep(fun model -> fn().Invoke(model))

member this.Run(result: MvuComponentBodyStep<'msg, 'model, 'marker>) =
let body =
MvuComponentBody(fun model ->
result.Invoke(unbox<'model> model).Compile()
)

MvuComponentBody(fun model -> result.Invoke(unbox<'model> model).Compile())

let data =
{ Program = this.Program
Arg = this.Arg
Body = body }

WidgetBuilder<unit, 'marker>(
MvuComponent.WidgetKey,
MvuComponent.Data.WithValue(data)
)

WidgetBuilder<unit, 'marker>(MvuComponent.WidgetKey, MvuComponent.Data.WithValue(data))
Loading

0 comments on commit dd2e4d0

Please sign in to comment.