diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index fcaa679276c0..fc2ca2a35c68 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -1858,6 +1858,26 @@ func installFileDropOverlay(on window: NSWindow, tabManager: TabManager) -> Bool return true } +private func installFileDropOverlayWhenReady( + on window: NSWindow, + tabManager: TabManager, + remainingAttempts: Int = 16 +) { + guard !installFileDropOverlay(on: window, tabManager: tabManager), + remainingAttempts > 0 else { return } + + // Defer retrying until the next main-loop turn so we don't mutate the + // NSThemeFrame hierarchy while SwiftUI/AppKit is still attaching views. + DispatchQueue.main.async { [weak window, weak tabManager] in + guard let window, let tabManager else { return } + installFileDropOverlayWhenReady( + on: window, + tabManager: tabManager, + remainingAttempts: remainingAttempts - 1 + ) + } +} + struct ContentView: View { @ObservedObject var updateViewModel: UpdateViewModel let windowId: UUID @@ -3796,12 +3816,7 @@ struct ContentView: View { sidebarState: sidebarState, sidebarSelectionState: sidebarSelectionState ) - })) - - // Install the file-drop overlay once the window hierarchy is ready, outside the - // high-frequency body-level window styling accessor above. - view = AnyView(view.background(WindowSetupAccessor { window in - installFileDropOverlay(on: window, tabManager: tabManager) + installFileDropOverlayWhenReady(on: window, tabManager: tabManager) })) return view diff --git a/Sources/WindowAccessor.swift b/Sources/WindowAccessor.swift index ecf32b0f76d2..a3d257a37fc5 100644 --- a/Sources/WindowAccessor.swift +++ b/Sources/WindowAccessor.swift @@ -36,48 +36,6 @@ struct WindowAccessor: NSViewRepresentable { } } -/// Replays the window callback until setup succeeds, then dedupes future updates for that window. -struct WindowSetupAccessor: NSViewRepresentable { - let onWindow: (NSWindow) -> Bool - - init(onWindow: @escaping (NSWindow) -> Bool) { - self.onWindow = onWindow - } - - func makeCoordinator() -> Coordinator { - Coordinator() - } - - func makeNSView(context: Context) -> WindowObservingView { - let view = WindowObservingView() - view.onWindow = { window in - guard context.coordinator.completedWindow !== window else { return } - if onWindow(window) { - context.coordinator.completedWindow = window - } - } - return view - } - - func updateNSView(_ nsView: WindowObservingView, context: Context) { - nsView.onWindow = { window in - guard context.coordinator.completedWindow !== window else { return } - if onWindow(window) { - context.coordinator.completedWindow = window - } - } - if let window = nsView.window { - nsView.onWindow?(window) - } - } -} - -extension WindowSetupAccessor { - final class Coordinator { - weak var completedWindow: NSWindow? - } -} - extension WindowAccessor { final class Coordinator { weak var lastWindow: NSWindow?