Skip to content

Commit f506d94

Browse files
committed
Support disabling float on top when the chat panel is detached
1 parent f01fc4c commit f506d94

3 files changed

Lines changed: 82 additions & 44 deletions

File tree

Core/Sources/SuggestionWidget/ChatPanelWindow.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,81 @@
11
import AppKit
2+
import ChatTab
3+
import Combine
4+
import ComposableArchitecture
25
import Foundation
36
import SwiftUI
47

58
final class ChatPanelWindow: NSWindow {
69
override var canBecomeKey: Bool { true }
710
override var canBecomeMain: Bool { true }
811

12+
private var cancellable: Set<AnyCancellable> = []
13+
914
var minimizeWindow: () -> Void = {}
1015

16+
init(
17+
store: StoreOf<ChatPanelFeature>,
18+
chatTabPool: ChatTabPool,
19+
minimizeWindow: @escaping () -> Void
20+
) {
21+
self.minimizeWindow = minimizeWindow
22+
super.init(
23+
contentRect: .zero,
24+
styleMask: [.resizable, .titled, .miniaturizable, .fullSizeContentView],
25+
backing: .buffered,
26+
defer: false
27+
)
28+
29+
titleVisibility = .hidden
30+
addTitlebarAccessoryViewController({
31+
let controller = NSTitlebarAccessoryViewController()
32+
let view = NSHostingView(rootView: ChatTitleBar(store: store))
33+
controller.view = view
34+
view.frame = .init(x: 0, y: 0, width: 100, height: 40)
35+
controller.layoutAttribute = .right
36+
return controller
37+
}())
38+
titlebarAppearsTransparent = true
39+
isReleasedWhenClosed = false
40+
isOpaque = false
41+
backgroundColor = .clear
42+
level = .init(NSWindow.Level.floating.rawValue + 1)
43+
collectionBehavior = [
44+
.fullScreenAuxiliary,
45+
.transient,
46+
.fullScreenPrimary,
47+
.fullScreenAllowsTiling,
48+
]
49+
hasShadow = true
50+
contentView = NSHostingView(
51+
rootView: ChatWindowView(
52+
store: store,
53+
toggleVisibility: { [weak self] isDisplayed in
54+
guard let self else { return }
55+
self.isPanelDisplayed = isDisplayed
56+
}
57+
)
58+
.environment(\.chatTabPool, chatTabPool)
59+
)
60+
setIsVisible(true)
61+
isPanelDisplayed = false
62+
63+
let viewStore = ViewStore(store)
64+
viewStore.publisher
65+
.map(\.isDetached)
66+
.receive(on: DispatchQueue.main)
67+
.sink { [weak self] isDetached in
68+
guard let self else { return }
69+
if UserDefaults.shared.value(for: \.disableFloatOnTopWhenTheChatPanelIsDetached) {
70+
self.level = isDetached
71+
? .init(NSWindow.Level.floating.rawValue)
72+
: .init(NSWindow.Level.mainMenu.rawValue + 1)
73+
} else {
74+
self.level = .init(NSWindow.Level.mainMenu.rawValue + 1)
75+
}
76+
}.store(in: &cancellable)
77+
}
78+
1179
var isWindowHidden: Bool = false {
1280
didSet {
1381
alphaValue = isPanelDisplayed && !isWindowHidden ? 1 : 0

Core/Sources/SuggestionWidget/WidgetWindowsController.swift

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -638,53 +638,15 @@ public final class WidgetWindows {
638638
@MainActor
639639
lazy var chatPanelWindow = {
640640
let it = ChatPanelWindow(
641-
contentRect: .zero,
642-
styleMask: [.resizable, .titled, .miniaturizable, .fullSizeContentView],
643-
backing: .buffered,
644-
defer: false
645-
)
646-
it.minimizeWindow = { [weak self] in
647-
self?.store.send(.chatPanel(.hideButtonClicked))
648-
}
649-
it.titleVisibility = .hidden
650-
it.addTitlebarAccessoryViewController({
651-
let controller = NSTitlebarAccessoryViewController()
652-
let view = NSHostingView(rootView: ChatTitleBar(store: store.scope(
641+
store: store.scope(
653642
state: \.chatPanelState,
654643
action: WidgetFeature.Action.chatPanel
655-
)))
656-
controller.view = view
657-
view.frame = .init(x: 0, y: 0, width: 100, height: 40)
658-
controller.layoutAttribute = .right
659-
return controller
660-
}())
661-
it.titlebarAppearsTransparent = true
662-
it.isReleasedWhenClosed = false
663-
it.isOpaque = false
664-
it.backgroundColor = .clear
665-
it.level = .init(NSWindow.Level.floating.rawValue + 1)
666-
it.collectionBehavior = [
667-
.fullScreenAuxiliary,
668-
.transient,
669-
.fullScreenPrimary,
670-
.fullScreenAllowsTiling,
671-
]
672-
it.hasShadow = true
673-
it.contentView = NSHostingView(
674-
rootView: ChatWindowView(
675-
store: store.scope(
676-
state: \.chatPanelState,
677-
action: WidgetFeature.Action.chatPanel
678-
),
679-
toggleVisibility: { [weak it] isDisplayed in
680-
guard let window = it else { return }
681-
window.isPanelDisplayed = isDisplayed
682-
}
683-
)
684-
.environment(\.chatTabPool, chatTabPool)
644+
),
645+
chatTabPool: chatTabPool,
646+
minimizeWindow: { [weak self] in
647+
self?.store.send(.chatPanel(.hideButtonClicked))
648+
}
685649
)
686-
it.setIsVisible(true)
687-
it.isPanelDisplayed = false
688650
it.delegate = controller
689651
return it
690652
}()

Tool/Sources/Preferences/Keys.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,14 @@ public extension UserDefaultPreferenceKeys {
469469
var preferredChatModelIdForWebScope: PreferenceKey<String> {
470470
.init(defaultValue: "", key: "PreferredChatModelIdForWebScope")
471471
}
472+
473+
var disableFloatOnTopWhenTheChatPanelIsDetached: PreferenceKey<Bool> {
474+
.init(defaultValue: false, key: "DisableFloatOnTopWhenTheChatPanelIsDetached")
475+
}
476+
477+
var keepFloatOnTopIfChatPanelAndXcodeOverlaps: PreferenceKey<Bool> {
478+
.init(defaultValue: true, key: "KeepFloatOnTopIfChatPanelAndXcodeOverlaps")
479+
}
472480
}
473481

474482
// MARK: - Theme

0 commit comments

Comments
 (0)