Skip to content

sketch for adaptive funcui #21

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions src/Avalonia.FuncUI/Avalonia.FuncUI.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

<ItemGroup>
<PackageReference Include="Avalonia.Desktop" Version="0.8.0" />
<PackageReference Include="FSharp.Data.Adaptive" Version="0.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
67 changes: 49 additions & 18 deletions src/Avalonia.FuncUI/Core/Types.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Avalonia.FuncUI

open System
open FSharp.Data.Adaptive

module rec Types =

Expand All @@ -10,68 +11,98 @@ module rec Types =
Value : obj
}

type AdaptivePropertyAttr =
{
Name : string
Value : aval<obj>
}

[<CustomEquality; NoComparison>]
type AttachedPropertyAttr =
{
Name : string
Value : obj
Handler : obj * (obj option) -> unit
}
with
override this.GetHashCode() =
(this.Name, this.Value).GetHashCode()
override this.GetHashCode() =
(this.Name, this.Value).GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()
override this.Equals other =
this.GetHashCode() = other.GetHashCode()

[<CustomEquality; NoComparison>]
type EventAttr =
{
Name : string
Value : Delegate
}
with
override this.GetHashCode() =
this.Name.GetHashCode()
override this.GetHashCode() =
this.Name.GetHashCode()

override this.Equals other =
// TODO: find a better way to do this
false // always set event
override this.Equals other =
// TODO: find a better way to do this
false // always set event

type ContentAttr =
{
Name : string
Content : ViewContent
}

type AdaptiveContentAttr =
{
Name : string
Content : AdaptiveViewContent
}

type ViewContent =
| Single of View option
| Multiple of View list

type AdaptiveViewContent =
| Single of AdaptiveView option
| Multiple of alist<AdaptiveView>

type Lifecycle = OnCreate | OnUpdate

[<CustomEquality; NoComparison>]
type LifecylceAttr =
type LifecycleAttr =
{
Lifecylce : Lifecycle
Func : obj -> unit
}
with
override this.GetHashCode() =
this.Lifecylce.GetHashCode()
override this.GetHashCode() =
this.Lifecylce.GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()
type AdaptiveLifecylceAttr = LifecycleAttr

type Attr =
| Property of PropertyAttr
| AttachedProperty of AttachedPropertyAttr
| Event of EventAttr
| Content of ContentAttr
| Lifecycle of LifecylceAttr
| Lifecycle of LifecycleAttr

[<RequireQualifiedAccess>]
type AdaptiveAttr =
| Property of AdaptivePropertyAttr
| AttachedProperty of AttachedPropertyAttr
| Event of EventAttr
| Content of AdaptiveContentAttr
| Lifecycle of AdaptiveLifecylceAttr

type View =
{
ViewType: Type
Attrs: Attr list
}

[<RequireQualifiedAccess>]
type AdaptiveView =
{
ViewType: aval<Type>
Attrs: alist<Attr>
}
96 changes: 69 additions & 27 deletions src/Avalonia.FuncUI/Core/VirtualDom.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,18 @@ module rec VirtualDom =
Value : obj option
Handler : obj * (obj option) -> unit
}
with
override this.GetHashCode() =
(this.Name, this.Value).GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()

static member From (property: AttachedPropertyAttr) : AttachedPropertyAttrDelta =
{
Name = property.Name
Value = Some property.Value
Handler = property.Handler
}
override this.GetHashCode() =
(this.Name, this.Value).GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()

static member From (property: AttachedPropertyAttr) : AttachedPropertyAttrDelta =
{
Name = property.Name
Value = Some property.Value
Handler = property.Handler
}

[<CustomEquality; NoComparison>]
type EventAttrDelta =
Expand All @@ -51,23 +50,23 @@ module rec VirtualDom =
OldValue : Delegate
NewValue : Delegate
}
with
override this.GetHashCode() =
this.Name.GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()

static member From (event: EventAttr) : EventAttrDelta =
{
Name = event.Name
OldValue = null
NewValue = event.Value
}
override this.GetHashCode() =
this.Name.GetHashCode()

override this.Equals other =
this.GetHashCode() = other.GetHashCode()

static member From (event: EventAttr) : EventAttrDelta =
{
Name = event.Name
OldValue = null
NewValue = event.Value
}

type ViewContentDelta =
| Single of ViewDelta option
| Multiple of ViewDelta list

static member From (viewContent: ViewContent) : ViewContentDelta =
match viewContent with
| ViewContent.Single single ->
Expand All @@ -93,12 +92,15 @@ module rec VirtualDom =
| AttachedPropertyDelta of AttachedPropertyAttrDelta
| EventDelta of EventAttrDelta
| ContentDelta of ContentAttrDelta
| LifecycleDelta

static member From (attr: Attr) : AttrDelta =
match attr with
| Property delta -> AttrDelta.PropertyDelta (PropertyAttrDelta.From delta)
| AttachedProperty delta -> AttrDelta.AttachedPropertyDelta (AttachedPropertyAttrDelta.From delta)
| Event delta -> AttrDelta.EventDelta (EventAttrDelta.From delta)
| Content delta -> AttrDelta.ContentDelta (ContentAttrDelta.From delta)
| Lifecycle delta -> AttrDelta.LifecycleDelta

type ViewDelta =
{
Expand Down Expand Up @@ -364,6 +366,38 @@ module rec VirtualDom =
else
ViewDelta.From next

module ViewDelta =
open Delta
let monoid = { FSharp.Data.Traceable.mempty = { ViewDelta.ViewType = null; ViewDelta.Attrs = [] }
FSharp.Data.Traceable.misEmpty = (fun x -> isNull x.ViewType)
FSharp.Data.Traceable.Monoid.mappend = (fun a b -> failwith "no append") }
module Reader =
open Delta
open Differ
open FSharp.Data.Adaptive
open FSharp.Data.Traceable

type ViewReader(view: AdaptiveView) =
inherit AbstractReader<ViewDelta>(ViewDelta.monoid)

let reader = view.Attrs.GetReader()
let mutable lastAttrs = []
override x.Compute(tok) =
let nextType = view.ViewType.GetValue(tok)
//TODO: use reader for incremental diff
let nextAttrs = view.Attrs.Content.GetValue(tok).AsList
let l = lastAttrs
lastAttrs <- nextAttrs
// TODO: use PropertyReader to incrementally get diff
let propertyAttrs = AttrDiffer.propertyDiffer(lastAttrs, nextAttrs)
//let attachedPropertyAttrs = AttrDiffer.attachedPropertyDiffer(last.Attrs, next.Attrs)
//let eventAttrs = AttrDiffer.eventDiffer(last.Attrs, next.Attrs)
//let contentAttrs = AttrDiffer.contentDiffer(last.Attrs, next.Attrs)
{
ViewType = nextType
Attrs = propertyAttrs //@ attachedPropertyAttrs @ eventAttrs @ contentAttrs
}

module Patcher =
open Delta

Expand Down Expand Up @@ -525,4 +559,12 @@ module rec VirtualDom =
| EventDelta event -> AttrPatcher.patchEvent view event

let createView (view: View) : Avalonia.Controls.IControl =
Activator.CreateInstance(view.ViewType) :?> Avalonia.Controls.IControl
Activator.CreateInstance(view.ViewType) :?> Avalonia.Controls.IControl


let adaptivePatch (view: Avalonia.Controls.IControl, viewElement: AdaptiveView) : (unit -> unit) =
let reader = Reader.ViewReader(viewElement)
fun () ->
let delta = reader.GetChanges(FSharp.Data.Adaptive.AdaptiveToken.Top)
Patcher.patch(view, delta)

2 changes: 1 addition & 1 deletion src/Avalonia.FuncUI/DSL.base.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type TypedAttr<'t> =
| AttachedProperty of AttachedPropertyAttr
| Event of EventAttr
| Content of ContentAttr
| Lifecycle of LifecylceAttr
| Lifecycle of LifecycleAttr

[<AbstractClass; Sealed>]
type Views () =
Expand Down