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
41 changes: 41 additions & 0 deletions Sources/OpenSwiftUI/Accessibility/AccessibilityNodeList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import OpenGraphShims

// FIXME
struct AccessibilityNodeList {
var nodes: [AccessibilityNode]
var version: DisplayList.Version
}

class AccessibilityNode {
// TODO
}

// TODO
struct AccessibilityNodeProxy {
static func makeProxyForIdentifiedView(
with list: AccessibilityNodeList?,
environment: EnvironmentValues
) -> AccessibilityNodeProxy? {
nil
}
}

// MARK: - AccessibilityNodesKey [6.4.41]

struct AccessibilityNodesKey: PreferenceKey {
static let defaultValue = AccessibilityNodeList(nodes: [], version: .init())

static func reduce(value: inout AccessibilityNodeList, nextValue: () -> AccessibilityNodeList) {
let next = nextValue()
value.version.combine(with: next.version)
value.nodes.append(contentsOf: next.nodes)
}
}

extension _ViewOutputs {
@inline(__always)
var accessibilityNodes: Attribute<AccessibilityNodeList>? {
get { self[AccessibilityNodesKey.self] }
set { self[AccessibilityNodesKey.self] = newValue }
}
}
6 changes: 6 additions & 0 deletions Sources/OpenSwiftUI/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#if os(iOS)
import UIKit
typealias DelegateBaseClass = UIResponder
typealias PlatformApplication = UIApplication
typealias PlatformApplicationDelegate = UIApplicationDelegate
#elseif os(macOS)
import AppKit
typealias DelegateBaseClass = NSResponder
typealias PlatformApplication = NSApplication
typealias PlatformApplicationDelegate = NSApplicationDelegate
#else
import Foundation
// FIXME: Temporarily use NSObject as a placeholder
typealias DelegateBaseClass = NSObject
typealias PlatformApplication = NSObject
typealias PlatformApplicationDelegate = AnyObject
#endif

class AppDelegate: DelegateBaseClass {
Expand Down
76 changes: 52 additions & 24 deletions Sources/OpenSwiftUI/App/OpenSwiftUIApplication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,63 @@ import Foundation
#endif

func runApp(_ app: some App) -> Never {
let graph = AppGraph(app: app)
graph.startProfilingIfNecessary()
graph.instantiate()
AppGraph.shared = graph
KitRendererCommon()
// let graph = AppGraph(app: app)
// graph.startProfilingIfNecessary()
// graph.instantiate()
// AppGraph.shared = graph
Update.ensure {
KitRendererCommon(AppDelegate.self)
}
}

private func KitRendererCommon() -> Never {
let argc = CommandLine.argc
let argv = CommandLine.unsafeArgv
// MARK: - runTestingApp [6.4.41] [iOS]

#if canImport(Darwin)
#if os(iOS) || os(tvOS) || os(macOS)
let principalClassName = NSStringFromClass(OpenSwiftUIApplication.self)
#endif
let delegateClassName = NSStringFromClass(AppDelegate.self)
func runTestingApp<V1, V2>(rootView: V1, comparisonView: V2, didLaunch: @escaping (any TestHost, any TestHost) -> ()) -> Never where V1: View, V2: View {
#if os(iOS)
TestingSceneDelegate.connectCallback = { (window: UIWindow, comparisonWindow: UIWindow) in
CoreTesting.isRunning = true
let rootVC = UIHostingController(rootView: rootView)
window.rootViewController = rootVC
window.makeKeyAndVisible()
let host = rootVC.host
TestingAppDelegate.testHost = host
let comparisonVC = UIHostingController(rootView: comparisonView)
comparisonWindow.rootViewController = comparisonVC
comparisonWindow.makeKeyAndVisible()
comparisonWindow.isHidden = false
comparisonWindow.isHidden = true
let comparisonHost = comparisonVC.host
TestingAppDelegate.comparisonHost = comparisonHost
didLaunch(host, comparisonHost)
}
#endif
KitRendererCommon(TestingAppDelegate.self)
}

#if os(iOS) || os(tvOS)
let code = UIApplicationMain(argc, argv, principalClassName, delegateClassName)
#elseif os(watchOS)
let code = WKApplicationMain(argc, argv, delegateClassName)
#elseif os(macOS)
// FIXME
let code = NSApplicationMain(argc, argv)
#else
let code: Int32 = 1
#endif
exit(code)

private func KitRendererCommon(_ delegateType: AnyObject.Type) -> Never {
let closure = { (argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>) in
let argc = CommandLine.argc
#if canImport(Darwin)
#if os(iOS) || os(tvOS) || os(macOS)
let principalClassName = NSStringFromClass(OpenSwiftUIApplication.self)
#endif
let delegateClassName = NSStringFromClass(delegateType)
#endif

#if os(iOS) || os(tvOS)
let code = UIApplicationMain(argc, argv, principalClassName, delegateClassName)
#elseif os(watchOS)
let code = WKApplicationMain(argc, argv, delegateClassName)
#elseif os(macOS)
// FIXME
let code = NSApplicationMain(argc, argv)
#else
let code: Int32 = 1
#endif
return exit(code)
}
return closure(CommandLine.unsafeArgv)
}

#if canImport(Darwin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ open class NSHostingController<Content>: NSViewController where Content: View {


public func _forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
host._forEachIdentifiedView(body: body)
host.forEachIdentifiedView(body: body)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,6 @@ open class NSHostingView<Content>: NSView, XcodeViewDebugDataProvider where Cont
// TODO
}

// FIXME
func _forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
let tree = preferenceValue(_IdentifiedViewsKey.self)
let adjustment = { [weak self](rect: inout CGRect) in
guard let self else { return }
rect = convert(rect, from: nil)
}
tree.forEach { proxy in
var proxy = proxy
proxy.adjustment = adjustment
body(proxy)
}
}

package func makeViewDebugData() -> Data? {
Update.ensure {
_ViewDebug.serializedData(viewGraph.viewDebugData())
Expand Down Expand Up @@ -423,4 +409,19 @@ extension NSHostingView: HostingViewProtocol {
anchor.convert(to: viewGraph.transform)
}
}

extension NSHostingView/*: TestHost*/ {
func forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
let tree = preferenceValue(_IdentifiedViewsKey.self)
let adjustment = { [weak self](rect: inout CGRect) in
guard let self else { return }
rect = convert(rect, from: nil)
}
tree.forEach { proxy in
var proxy = proxy
proxy.adjustment = adjustment
body(proxy)
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ open class UIHostingController<Content>: UIViewController where Content : View {
}

public func _forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
host._forEachIdentifiedView(body: body)
host.forEachIdentifiedView(body: body)
}

@available(*, deprecated, message: "Use UIHostingController/safeAreaRegions or _UIHostingView/safeAreaRegions")
Expand Down
159 changes: 144 additions & 15 deletions Sources/OpenSwiftUI/Integration/Hosting/UIKit/View/UIHostingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,21 @@ open class _UIHostingView<Content>: UIView, XcodeViewDebugDataProvider where Con
frameDidChange(oldValue: oldValue)
}
}


open override var bounds: CGRect {
get {
super.bounds
}
set {
guard allowFrameChanges else {
return
}
let oldValue = super.bounds
super.bounds = newValue
frameDidChange(oldValue: oldValue)
}
}

// TODO

func setRootView(_ view: Content, transaction: Transaction) {
Expand Down Expand Up @@ -417,20 +431,6 @@ open class _UIHostingView<Content>: UIView, XcodeViewDebugDataProvider where Con
func clearUpdateTimer() {
}

// FIXME
func _forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
let tree = preferenceValue(_IdentifiedViewsKey.self)
let adjustment = { [weak self](rect: inout CGRect) in
guard let self else { return }
rect = convert(rect, from: nil)
}
tree.forEach { proxy in
var proxy = proxy
proxy.adjustment = adjustment
body(proxy)
}
}

@_spi(Private)
@available(iOS, deprecated, message: "Use UIHostingController/safeAreaRegions or _UIHostingView/safeAreaRegions")
final public var addsKeyboardToSafeAreaInsets: Bool {
Expand Down Expand Up @@ -706,4 +706,133 @@ extension _UIHostingView: HostingViewProtocol {
}
}

// MARK: - _UIHostingView + TestHost [6.4.41]

extension _UIHostingView: TestHost {
package func setTestSize(_ size: CGSize) {
let newSize: CGSize
if size == CGSize.deviceSize {
let screenSize = UIDevice.current.screenSize
let idiom = UIDevice.current.userInterfaceIdiom
if idiom == .pad, screenSize.width < screenSize.height {
newSize = CGSize(width: screenSize.height, height: screenSize.width)
} else {
if idiom == .phone, screenSize.height < screenSize.width {
newSize = CGSize(width: screenSize.height, height: screenSize.width)
} else {
newSize = screenSize
}
}
} else {
newSize = size
}
if bounds.size != newSize {
allowFrameChanges = true
bounds.size = newSize
allowFrameChanges = false
}
}

package func setTestSafeAreaInsets(_ insets: EdgeInsets) {
explicitSafeAreaInsets = insets

}

package var testSize: CGSize { bounds.size }

package var viewCacheIsEmpty: Bool {
Update.locked {
renderer.viewCacheIsEmpty
}
}

package func forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
let tree = preferenceValue(_IdentifiedViewsKey.self)
tree.forEach { proxy in
var proxy = proxy
proxy.adjustment = { [weak self] rect in
guard let self else { return }
rect = convert(rect, from: nil)
}
body(proxy)
}
}

package func forEachDescendantHost(body: (any TestHost) -> Void) {
forEachDescendantHost { (view: UIView) in
if let testHost = view as? any TestHost {
body(testHost)
}
}
}

package func renderForTest(interval: Double) {
_renderForTest(interval: interval)
}

package var attributeCountInfo: AttributeCountTestInfo {
preferenceValue(AttributeCountInfoKey.self)
}

public func _renderForTest(interval: Double) {
func shouldContinue() -> Bool {
if propertiesNeedingUpdate == [], !CoreTesting.needRender {
false
} else {
times >= 0
}
}
advanceTimeForTest(interval: interval)
canAdvanceTimeAutomatically = false
var times = 16
repeat {
times -= 1
CoreTesting.needRender = false
updateGraph { host in
host.flushTransactions()
}
RunLoop.flushObservers()
render(targetTimestamp: nil)
CATransaction.flush()
} while shouldContinue()
CoreTesting.needRender = false
canAdvanceTimeAutomatically = true
}
}

extension UIDevice {
package var screenSize: CGSize {
let screenBounds = UIScreen.main.bounds
let screenWidth = screenBounds.width
let screenHeight = screenBounds.height
let orientation = UIDevice.current.orientation
let finalWidth: CGFloat
let finalHeight: CGFloat
switch orientation {
case .landscapeLeft, .landscapeRight:
// In landscape, swap dimensions to ensure width > height
finalWidth = max(screenWidth, screenHeight)
finalHeight = min(screenWidth, screenHeight)
case .portrait, .portraitUpsideDown:
// In portrait, keep original dimensions (height > width)
finalWidth = screenWidth
finalHeight = screenHeight
default:
// For other orientations, keep original dimensions
finalWidth = screenWidth
finalHeight = screenHeight
}
return CGSize(width: finalWidth, height: finalHeight)
}
}

extension UIView {
func forEachDescendantHost(body: (UIView) -> Void) {
body(self)
for view in subviews {
view.forEachDescendantHost(body: body)
}
}
}

#endif
Loading