@@ -5,10 +5,12 @@ import Foundation
55import SwiftUI
66import XcodeInspector
77
8+ @MainActor
89public protocol IDEWorkspaceWindowOverlayWindowControllerContentProvider {
910 associatedtype Content : View
1011 func createWindow( ) -> NSWindow ?
1112 func createContent( ) -> Content
13+ func destroy( )
1214
1315 init ( windowInspector: WorkspaceXcodeWindowInspector , application: NSRunningApplication )
1416}
@@ -38,64 +40,60 @@ final class IDEWorkspaceWindowOverlayWindowController {
3840 ) {
3941 self . inspector = inspector
4042 self . application = application
41- contentProviders = contentProviderFactory ( inspector, application)
42-
43- // Create the invisible panel
44- let panel = NSPanel (
45- contentRect: . zero,
46- styleMask: [ . borderless] ,
47- backing: . buffered,
48- defer: false
49- )
50- panel. isReleasedWhenClosed = false
51- panel. isOpaque = false
52- panel. backgroundColor = . clear
53- panel. hasShadow = false
54- panel. ignoresMouseEvents = true
55- panel. alphaValue = 0
56- panel. collectionBehavior = [ . canJoinAllSpaces, . transient]
57- panel. level = widgetLevel ( 0 )
58- panel. setIsVisible ( true )
59- maskPanel = panel
60-
61- panel. contentView = NSHostingView (
62- rootView: ZStack {
43+ let contentProviders = contentProviderFactory ( inspector, application)
44+ self . contentProviders = contentProviders
45+
46+ let panel = OverlayPanel (
47+ contentRect: . init( x: 0 , y: 0 , width: 200 , height: 200 )
48+ ) {
49+ ZStack {
50+ Text ( " Hello World " )
51+ . padding ( )
52+ . background (
53+ Rectangle ( )
54+ . fill ( . black)
55+ . opacity ( 0.3 )
56+ )
6357 ForEach ( 0 ..< contentProviders. count, id: \. self) { ( index: Int ) in
64- self . contentProviders [ index] . contentBody
58+ contentProviders [ index] . contentBody
6559 }
6660 }
61+ . background {
62+ Rectangle ( ) . fill ( . green. opacity ( 0.2 ) )
63+ }
6764 . allowsHitTesting ( false )
68- )
69-
65+ }
66+ maskPanel = panel
67+
7068 for contentProvider in contentProviders {
7169 if let window = contentProvider. createWindow ( ) {
7270 panel. addChildWindow ( window, ordered: . above)
7371 }
7472 }
7573
76- // Listen to AX notifications for window move/resize
7774 let windowElement = inspector. uiElement
7875 let stream = AXNotificationStream (
7976 app: application,
8077 element: windowElement,
8178 notificationNames: kAXMovedNotification, kAXResizedNotification
8279 )
8380
84- axNotificationTask = Task { [ weak self ] in
81+ axNotificationTask = Task { [ weak panel ] in
8582 for await notification in stream {
86- guard let self else { return }
83+ guard let panel else { return }
84+ if Task . isCancelled { return }
8785 switch notification. name {
8886 case kAXMovedNotification, kAXResizedNotification:
8987 if let rect = windowElement. rect {
90- self . maskPanel . setFrame ( rect, display: false )
88+ panel . setFrame ( rect, display: false )
9189 }
9290 default : continue
9391 }
9492 }
9593 }
9694
9795 if let rect = windowElement. rect {
98- maskPanel . setFrame ( rect, display: false )
96+ panel . setFrame ( rect, display: false )
9997 }
10098 }
10199
@@ -111,40 +109,29 @@ final class IDEWorkspaceWindowOverlayWindowController {
111109 }
112110 }
113111
114- /// Make the window the top most window and visible.
115112 func access( ) {
116113 lastAccessDate = Date ( )
117- maskPanel. level = widgetLevel ( 0 )
114+ maskPanel. level = overlayLevel ( 0 )
118115 maskPanel. setIsVisible ( true )
119116 maskPanel. orderFrontRegardless ( )
120117 }
121118
122- /// Stop keeping the window the top most window, do not change visibility.
123119 func dim( ) {
124120 maskPanel. level = . normal
125121 }
126122
127- /// Hide the window.
128123 func hide( ) {
129124 maskPanel. setIsVisible ( false )
130125 maskPanel. level = . normal
131126 }
132127
133- /// Destroy the controller and clean up resources.
134128 func destroy( ) {
135129 axNotificationTask? . cancel ( )
136130 maskPanel. close ( )
131+ for contentProvider in contentProviders {
132+ contentProvider. destroy ( )
133+ }
137134 isDestroyed = true
138135 }
139136}
140137
141- func widgetLevel( _ addition: Int ) -> NSWindow . Level {
142- let minimumWidgetLevel : Int
143- #if DEBUG
144- minimumWidgetLevel = NSWindow . Level. floating. rawValue + 1
145- #else
146- minimumWidgetLevel = NSWindow . Level. floating. rawValue
147- #endif
148- return . init( minimumWidgetLevel + addition)
149- }
150-
0 commit comments