Skip to content
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
97 changes: 64 additions & 33 deletions Sources/ElementaryUI/HTMLViews/_ElementNode.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
enum _ElementAttributes {
case inline(node: DOM.Node, lastApplied: _AttributeStorage)
case modifier(_AttributeModifier)
}

public struct _ElementNode<Child: _Reconcilable & ~Copyable>:
~Copyable,
_Reconcilable
Expand Down Expand Up @@ -32,10 +27,10 @@ public struct _ElementNode<Child: _Reconcilable & ~Copyable>:
ctx.appendStaticElement(domNode)

guard !viewContext.hasNoUpstreamModifiers else {
addHTMLAttributes(attributes, to: domNode, using: ctx.dom)
self.attributes = .inline(
node: domNode,
lastApplied: attributes
self.attributes = .mountInline(
attributes,
on: domNode,
using: ctx.dom
)
self.child = ctx.withChildContext {
(mctx: consuming _MountContext) in
Expand All @@ -47,19 +42,18 @@ public struct _ElementNode<Child: _Reconcilable & ~Copyable>:
}

var childContext = copy viewContext

if childContext.modifiers[_AttributeModifier.key] != nil {
let modifier = _AttributeModifier(
value: attributes,
upstream: childContext.modifiers
)
self.attributes = .modifier(modifier)
self.attributes = .mountModifier(modifier)
childContext.modifiers[_AttributeModifier.key] = modifier
} else {
addHTMLAttributes(attributes, to: domNode, using: ctx.dom)
self.attributes = .inline(
node: domNode,
lastApplied: attributes
self.attributes = .mountInline(
attributes,
on: domNode,
using: ctx.dom
)
}

Expand Down Expand Up @@ -87,24 +81,7 @@ public struct _ElementNode<Child: _Reconcilable & ~Copyable>:
_ context: inout _TransactionContext,
block: (inout Child, inout _TransactionContext) -> Void
) {
switch self.attributes {
case .modifier(let modifier):
modifier.updateValue(attributes, &context)
case .inline(let node, let lastApplied):
if attributes != lastApplied {
context.scheduler.addCommitAction(
.patchAttributes(
node: node,
from: lastApplied,
to: attributes
)
)
self.attributes = .inline(
node: node,
lastApplied: attributes
)
}
}
self.attributes.patch(attributes, context: &context)
block(&child, &context)
}

Expand All @@ -123,3 +100,57 @@ private extension _ViewContext {
modifiers.isEmpty && layoutObservers.isEmpty
}
}

struct _ElementAttributes {
private enum Storage {
case inline(node: DOM.Node, lastApplied: _AttributeStorage)
case modifier(_AttributeModifier)
}

private var storage: Storage

private init(storage: consuming Storage) {
self.storage = storage
}

static func mountInline(
_ attributes: _AttributeStorage,
on node: DOM.Node,
using dom: DOMInteractor
) -> Self {
addHTMLAttributes(attributes, to: node, using: dom)
return Self(
storage: .inline(node: node, lastApplied: attributes)
)
}

static func mountModifier(
_ modifier: consuming _AttributeModifier
) -> Self {
Self(storage: .modifier(modifier))
}

@inline(never)
mutating func patch(
_ attributes: _AttributeStorage,
context: inout _TransactionContext
) {
switch storage {
case .modifier(let modifier):
modifier.updateValue(attributes, &context)
case .inline(let node, let lastApplied):
guard attributes != lastApplied else { return }
context.scheduler.addCommitAction(
.patchAttributes(
node: node,
from: lastApplied,
to: attributes
)
)
storage = .inline(
node: node,
lastApplied: attributes
)
}
}
}
53 changes: 17 additions & 36 deletions Sources/ElementaryUI/Transition/_TransitionableNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ public struct _TransitionableNode<Node: _Reconcilable & ~Copyable>:
~Copyable,
_Reconcilable
{
private enum Storage: ~Copyable {
case direct(Node)
case transitioned(_TransitionElement)
case _movedOut
}

private var storage: Storage
// FIXME: this should be an enum, but 6.3 has serious embedded miscompiles with enums and ownership
// revisit in 6.4
private var node: Node?
private var transitionedElement: _TransitionElement?

init(
context: borrowing _ViewContext,
Expand All @@ -21,8 +18,13 @@ public struct _TransitionableNode<Node: _Reconcilable & ~Copyable>:
inout _MountContext
) -> Node
) {
defer {
precondition(node != nil || transitionedElement != nil)
precondition(node == nil || transitionedElement == nil)
}

guard let transition = context.transition else {
storage = .direct(makeNode(context, &ctx))
self.node = makeNode(context, &ctx)
return
}

Expand All @@ -33,58 +35,37 @@ public struct _TransitionableNode<Node: _Reconcilable & ~Copyable>:

// Without a structural owner there is nothing that can defer removal.
guard ctx.slotTransitions != nil else {
storage = .direct(makeNode(nodeContext, &ctx))
self.node = makeNode(nodeContext, &ctx)
return
}

let element = _TransitionElement.make(
self.transitionedElement = _TransitionElement.make(
transition: transition.value,
context: nodeContext,
ctx: &ctx,
makeElement: { context, ctx in
AnyReconcilable(makeNode(context, &ctx))
}
)

storage = .transitioned(element)
}

mutating func update(
_ tx: inout _TransactionContext,
body: (inout Node, inout _TransactionContext) -> Void
) {
var storage = Storage._movedOut
swap(&storage, &self.storage)

switch consume storage {
case .direct(var node):
body(&node, &tx)
self.storage = .direct(node)
case .transitioned(let transitionedElement):
transitionedElement.forEachPlaceholder { placeholder in
if node != nil { body(&node!, &tx) }
if transitionedElement != nil {
transitionedElement!.forEachPlaceholder { placeholder in
placeholder.modify(as: Node.self) { node in
body(&node, &tx)
}
}
self.storage = .transitioned(transitionedElement)
case ._movedOut:
preconditionFailure(
"_TransitionableNode storage was already moved out"
)
}
}

public consuming func unmount(_ context: inout _CommitContext) {
switch consume storage {
case .direct(let node):
node.unmount(&context)
case .transitioned(let transitionedElement):
transitionedElement.unmount(&context)
case ._movedOut:
preconditionFailure(
"_TransitionableNode storage was already moved out"
)
}
node.take()?.unmount(&context)
transitionedElement.take()?.unmount(&context)
}
}

Expand Down
Loading