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
27 changes: 21 additions & 6 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
42 changes: 0 additions & 42 deletions Sources/WindowAccessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
Loading