Skip to content

Commit e280dbd

Browse files
committed
Merge branch 'feature/improve-performance-of-widgets' into develop
2 parents c3d4dff + f3275a1 commit e280dbd

7 files changed

Lines changed: 712 additions & 663 deletions

File tree

Core/Sources/SuggestionWidget/FeatureReducers/ChatPanelFeature.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public struct ChatPanelFeature: ReducerProtocol {
8585
@Dependency(\.chatTabBuilderCollection) var chatTabBuilderCollection
8686

8787
@MainActor func toggleFullScreen() {
88-
let window = suggestionWidgetControllerDependency.windows
88+
let window = suggestionWidgetControllerDependency.windowsController?.windows
8989
.chatPanelWindow
9090
window?.toggleFullScreen(nil)
9191
}

Core/Sources/SuggestionWidget/FeatureReducers/PanelFeature.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public struct PanelFeature: ReducerProtocol {
3838
@Dependency(\.suggestionWidgetControllerDependency) var suggestionWidgetControllerDependency
3939
@Dependency(\.xcodeInspector) var xcodeInspector
4040
@Dependency(\.activateThisApp) var activateThisApp
41-
var windows: WidgetWindows { suggestionWidgetControllerDependency.windows }
41+
var windows: WidgetWindows? { suggestionWidgetControllerDependency.windowsController?.windows }
4242

4343
public var body: some ReducerProtocol<State, Action> {
4444
Scope(state: \.suggestionPanelState, action: /Action.suggestionPanel) {
@@ -122,7 +122,9 @@ public struct PanelFeature: ReducerProtocol {
122122

123123
if hasPromptToCode {
124124
activateThisApp()
125-
await windows.sharedPanelWindow.makeKey()
125+
await MainActor.run {
126+
windows?.sharedPanelWindow.makeKey()
127+
}
126128
}
127129
}.animation(.easeInOut(duration: 0.2))
128130

Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift

Lines changed: 26 additions & 378 deletions
Large diffs are not rendered by default.

Core/Sources/SuggestionWidget/ModuleDependency.swift

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,11 @@ public final class SuggestionWidgetControllerDependency {
1313
public var suggestionWidgetDataSource: SuggestionWidgetDataSource?
1414
public var onOpenChatClicked: () -> Void = {}
1515
public var onCustomCommandClicked: (CustomCommand) -> Void = { _ in }
16-
public var windows: WidgetWindows = .init()
16+
var windowsController: WindowsController?
1717

1818
public init() {}
1919
}
2020

21-
@MainActor
22-
public final class WidgetWindows {
23-
var fullscreenDetector: NSWindow!
24-
var widgetWindow: NSWindow!
25-
var sharedPanelWindow: NSWindow!
26-
var suggestionPanelWindow: NSWindow!
27-
var chatPanelWindow: ChatWindow!
28-
var toastWindow: NSWindow!
29-
30-
nonisolated
31-
init() {}
32-
33-
func orderFront() {
34-
widgetWindow?.orderFrontRegardless()
35-
toastWindow?.orderFrontRegardless()
36-
sharedPanelWindow?.orderFrontRegardless()
37-
suggestionPanelWindow?.orderFrontRegardless()
38-
chatPanelWindow?.orderFrontRegardless()
39-
}
40-
}
41-
4221
public final class WidgetUserDefaultsObservers {
4322
let presentationModeChangeObserver = UserDefaultsObserver(
4423
object: UserDefaults.shared,

Core/Sources/SuggestionWidget/SuggestionWidgetController.swift

Lines changed: 5 additions & 258 deletions
Original file line numberDiff line numberDiff line change
@@ -11,194 +11,10 @@ import XcodeInspector
1111

1212
@MainActor
1313
public final class SuggestionWidgetController: NSObject {
14-
// you should make these window `.transient` so they never show up in the mission control.
15-
16-
private lazy var fullscreenDetector = {
17-
let it = CanBecomeKeyWindow(
18-
contentRect: .zero,
19-
styleMask: .borderless,
20-
backing: .buffered,
21-
defer: false
22-
)
23-
it.isReleasedWhenClosed = false
24-
it.isOpaque = false
25-
it.backgroundColor = .clear
26-
it.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .transient]
27-
it.hasShadow = false
28-
it.setIsVisible(false)
29-
it.canBecomeKeyChecker = { false }
30-
return it
31-
}()
32-
33-
private lazy var widgetWindow = {
34-
let it = CanBecomeKeyWindow(
35-
contentRect: .zero,
36-
styleMask: .borderless,
37-
backing: .buffered,
38-
defer: false
39-
)
40-
it.isReleasedWhenClosed = false
41-
it.isOpaque = false
42-
it.backgroundColor = .clear
43-
it.level = .floating
44-
it.collectionBehavior = [.fullScreenAuxiliary, .transient]
45-
it.hasShadow = true
46-
it.contentView = NSHostingView(
47-
rootView: WidgetView(
48-
store: store.scope(
49-
state: \._circularWidgetState,
50-
action: WidgetFeature.Action.circularWidget
51-
)
52-
)
53-
)
54-
it.setIsVisible(true)
55-
it.canBecomeKeyChecker = { false }
56-
return it
57-
}()
58-
59-
private lazy var sharedPanelWindow = {
60-
let it = CanBecomeKeyWindow(
61-
contentRect: .init(x: 0, y: 0, width: Style.panelWidth, height: Style.panelHeight),
62-
styleMask: .borderless,
63-
backing: .buffered,
64-
defer: false
65-
)
66-
it.isReleasedWhenClosed = false
67-
it.isOpaque = false
68-
it.backgroundColor = .clear
69-
it.level = .init(NSWindow.Level.floating.rawValue + 2)
70-
it.collectionBehavior = [.fullScreenAuxiliary, .transient]
71-
it.hasShadow = true
72-
it.contentView = NSHostingView(
73-
rootView: SharedPanelView(
74-
store: store.scope(
75-
state: \.panelState,
76-
action: WidgetFeature.Action.panel
77-
).scope(
78-
state: \.sharedPanelState,
79-
action: PanelFeature.Action.sharedPanel
80-
)
81-
)
82-
)
83-
it.setIsVisible(true)
84-
it.canBecomeKeyChecker = { [store] in
85-
store.withState { state in
86-
state.panelState.sharedPanelState.content.promptToCode != nil
87-
}
88-
}
89-
return it
90-
}()
91-
92-
private lazy var suggestionPanelWindow = {
93-
let it = CanBecomeKeyWindow(
94-
contentRect: .init(x: 0, y: 0, width: Style.panelWidth, height: Style.panelHeight),
95-
styleMask: .borderless,
96-
backing: .buffered,
97-
defer: false
98-
)
99-
it.isReleasedWhenClosed = false
100-
it.isOpaque = false
101-
it.backgroundColor = .clear
102-
it.level = .init(NSWindow.Level.floating.rawValue + 2)
103-
it.collectionBehavior = [.fullScreenAuxiliary, .transient]
104-
it.hasShadow = true
105-
it.contentView = NSHostingView(
106-
rootView: SuggestionPanelView(
107-
store: store.scope(
108-
state: \.panelState,
109-
action: WidgetFeature.Action.panel
110-
).scope(
111-
state: \.suggestionPanelState,
112-
action: PanelFeature.Action.suggestionPanel
113-
)
114-
)
115-
)
116-
it.canBecomeKeyChecker = { false }
117-
it.setIsVisible(true)
118-
return it
119-
}()
120-
121-
private lazy var chatPanelWindow = {
122-
let it = ChatWindow(
123-
contentRect: .zero,
124-
styleMask: [.resizable, .titled, .miniaturizable, .fullSizeContentView],
125-
backing: .buffered,
126-
defer: false
127-
)
128-
it.minimizeWindow = { [weak self] in
129-
self?.store.send(.chatPanel(.hideButtonClicked))
130-
}
131-
it.titleVisibility = .hidden
132-
it.addTitlebarAccessoryViewController({
133-
let controller = NSTitlebarAccessoryViewController()
134-
let view = NSHostingView(rootView: ChatTitleBar(store: store.scope(
135-
state: \.chatPanelState,
136-
action: WidgetFeature.Action.chatPanel
137-
)))
138-
controller.view = view
139-
view.frame = .init(x: 0, y: 0, width: 100, height: 40)
140-
controller.layoutAttribute = .right
141-
return controller
142-
}())
143-
it.titlebarAppearsTransparent = true
144-
it.isReleasedWhenClosed = false
145-
it.isOpaque = false
146-
it.backgroundColor = .clear
147-
it.level = .init(NSWindow.Level.floating.rawValue + 1)
148-
it.collectionBehavior = [
149-
.fullScreenAuxiliary,
150-
.transient,
151-
.fullScreenPrimary,
152-
.fullScreenAllowsTiling,
153-
]
154-
it.hasShadow = true
155-
it.contentView = NSHostingView(
156-
rootView: ChatWindowView(
157-
store: store.scope(
158-
state: \.chatPanelState,
159-
action: WidgetFeature.Action.chatPanel
160-
),
161-
toggleVisibility: { [weak it] isDisplayed in
162-
guard let window = it else { return }
163-
window.isPanelDisplayed = isDisplayed
164-
}
165-
)
166-
.environment(\.chatTabPool, chatTabPool)
167-
)
168-
it.setIsVisible(true)
169-
it.isPanelDisplayed = false
170-
it.delegate = self
171-
return it
172-
}()
173-
174-
private lazy var toastWindow = {
175-
let it = CanBecomeKeyWindow(
176-
contentRect: .zero,
177-
styleMask: [.borderless],
178-
backing: .buffered,
179-
defer: false
180-
)
181-
it.isReleasedWhenClosed = false
182-
it.isOpaque = true
183-
it.backgroundColor = .clear
184-
it.level = .floating
185-
it.collectionBehavior = [.fullScreenAuxiliary, .transient]
186-
it.hasShadow = false
187-
it.contentView = NSHostingView(
188-
rootView: ToastPanelView(store: store.scope(
189-
state: \.toastPanel,
190-
action: WidgetFeature.Action.toastPanel
191-
))
192-
)
193-
it.setIsVisible(true)
194-
it.ignoresMouseEvents = true
195-
it.canBecomeKeyChecker = { false }
196-
return it
197-
}()
198-
19914
let store: StoreOf<WidgetFeature>
20015
let viewStore: ViewStoreOf<WidgetFeature>
20116
let chatTabPool: ChatTabPool
17+
let windowsController: WindowsController
20218
private var cancellable = Set<AnyCancellable>()
20319

20420
public let dependency: SuggestionWidgetControllerDependency
@@ -212,19 +28,16 @@ public final class SuggestionWidgetController: NSObject {
21228
self.store = store
21329
self.chatTabPool = chatTabPool
21430
viewStore = .init(store, observe: { $0 })
31+
windowsController = .init(store: store, chatTabPool: chatTabPool)
21532

21633
super.init()
21734

21835
if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return }
21936

220-
dependency.windows.chatPanelWindow = chatPanelWindow
221-
dependency.windows.toastWindow = toastWindow
222-
dependency.windows.sharedPanelWindow = sharedPanelWindow
223-
dependency.windows.suggestionPanelWindow = suggestionPanelWindow
224-
dependency.windows.fullscreenDetector = fullscreenDetector
225-
dependency.windows.widgetWindow = widgetWindow
226-
37+
dependency.windowsController = windowsController
38+
22739
store.send(.startup)
40+
windowsController.start()
22841
}
22942
}
23043

@@ -267,69 +80,3 @@ public extension SuggestionWidgetController {
26780
}
26881
}
26982

270-
// MARK: - NSWindowDelegate
271-
272-
extension SuggestionWidgetController: NSWindowDelegate {
273-
public func windowWillMove(_ notification: Notification) {
274-
guard (notification.object as? NSWindow) === chatPanelWindow else { return }
275-
Task { @MainActor in
276-
await Task.yield()
277-
store.send(.chatPanel(.detachChatPanel))
278-
}
279-
}
280-
281-
public func windowWillEnterFullScreen(_ notification: Notification) {
282-
guard (notification.object as? NSWindow) === chatPanelWindow else { return }
283-
Task { @MainActor in
284-
await Task.yield()
285-
store.send(.chatPanel(.enterFullScreen))
286-
}
287-
}
288-
289-
public func windowWillExitFullScreen(_ notification: Notification) {
290-
guard (notification.object as? NSWindow) === chatPanelWindow else { return }
291-
Task { @MainActor in
292-
await Task.yield()
293-
store.send(.chatPanel(.exitFullScreen))
294-
}
295-
}
296-
}
297-
298-
// MARK: - Window Subclasses
299-
300-
class CanBecomeKeyWindow: NSWindow {
301-
var canBecomeKeyChecker: () -> Bool = { true }
302-
override var canBecomeKey: Bool { canBecomeKeyChecker() }
303-
override var canBecomeMain: Bool { canBecomeKeyChecker() }
304-
}
305-
306-
class ChatWindow: NSWindow {
307-
override var canBecomeKey: Bool { true }
308-
override var canBecomeMain: Bool { true }
309-
310-
311-
var minimizeWindow: () -> Void = {}
312-
313-
var isWindowHidden: Bool = false {
314-
didSet {
315-
alphaValue = isPanelDisplayed && !isWindowHidden ? 1 : 0
316-
}
317-
}
318-
319-
var isPanelDisplayed: Bool = false {
320-
didSet {
321-
alphaValue = isPanelDisplayed && !isWindowHidden ? 1 : 0
322-
}
323-
}
324-
325-
override var alphaValue: CGFloat {
326-
didSet {
327-
ignoresMouseEvents = alphaValue <= 0
328-
}
329-
}
330-
331-
override func miniaturize(_: Any?) {
332-
minimizeWindow()
333-
}
334-
}
335-

Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import AppKit
22
import Foundation
33

4-
struct WidgetLocation {
5-
struct PanelLocation {
4+
public struct WidgetLocation: Equatable {
5+
struct PanelLocation: Equatable {
66
var frame: CGRect
77
var alignPanelTop: Bool
88
}

0 commit comments

Comments
 (0)