diff --git a/Sources/OpenSwiftUI/Event/Focus/FocusEventProxy.swift b/Sources/OpenSwiftUI/Event/Focus/FocusEventProxy.swift index 5ba4499b0..2cd34a2bc 100644 --- a/Sources/OpenSwiftUI/Event/Focus/FocusEventProxy.swift +++ b/Sources/OpenSwiftUI/Event/Focus/FocusEventProxy.swift @@ -3,12 +3,58 @@ // OpenSwiftUI // // Audited for 6.5.4 -// Status: WIP +// Status: Complete // ID: 220887A402CE7023D01D5BCC1E080716 (SwiftUI) +import OpenAttributeGraphShims @_spi(ForOpenSwiftUIOnly) import OpenSwiftUICore -// MARK: - FocusEventProxyResponder [TBA] +// MARK: - FocusEventProxyResponder -class FocusEventProxyResponder: DefaultLayoutViewResponder {} +private class FocusEventProxyResponder: DefaultLayoutViewResponder {} + +@inline(__always) +func asFocusEventProxyResponder(_ responder: ResponderNode) -> ViewResponder? { + responder as? FocusEventProxyResponder +} + +#if os(iOS) || os(visionOS) +// MARK: - FocusEventProxyModifier + +struct FocusEventProxyModifier: MultiViewModifier, PrimitiveViewModifier { + static func _makeView( + modifier: _GraphValue, + inputs: _ViewInputs, + body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs + ) -> _ViewOutputs { + var outputs = body(_Graph(), inputs) + guard IOSFocusEnabledFlag.evaluate(inputs: inputs.base), + inputs.preferences.requiresViewResponders else { + return outputs + } + outputs.preferences.viewResponders = Attribute(FocusEventProxyResponderFilter( + children: outputs.viewResponders(), + responder: FocusEventProxyResponder(inputs: inputs) + )) + return outputs + } +} + +// MARK: - FocusEventProxyResponderFilter + +private struct FocusEventProxyResponderFilter: StatefulRule { + @Attribute var children: [ViewResponder] + + let responder: FocusEventProxyResponder + + typealias Value = [ViewResponder] + + mutating func updateValue() { + responder.updateChildren($children.changedValue()) + if !hasValue { + value = [responder] + } + } +} +#endif diff --git a/Sources/OpenSwiftUI/Event/Focus/Focusable.swift b/Sources/OpenSwiftUI/Event/Focus/Focusable.swift index e0b4b2335..464512e36 100644 --- a/Sources/OpenSwiftUI/Event/Focus/Focusable.swift +++ b/Sources/OpenSwiftUI/Event/Focus/Focusable.swift @@ -403,7 +403,7 @@ private class FocusableViewResponder: DefaultLayoutViewResponder, FocusResponder var focusProxyResponder: ViewResponder? { var result: ViewResponder? visit { responder in - guard let responder = responder as? FocusEventProxyResponder else { return .next } + guard let responder = asFocusEventProxyResponder(responder) else { return .next } result = responder return .cancel }