Fix hidden titlebar underlap and settings focus
This commit is contained in:
parent
f592d97126
commit
d67237b891
3 changed files with 105 additions and 8 deletions
|
|
@ -9,6 +9,30 @@ import Combine
|
|||
import ObjectiveC.runtime
|
||||
import Darwin
|
||||
|
||||
final class MainWindowHostingView<Content: View>: NSHostingView<Content> {
|
||||
private let zeroSafeAreaLayoutGuide = NSLayoutGuide()
|
||||
|
||||
override var safeAreaInsets: NSEdgeInsets { NSEdgeInsetsZero }
|
||||
override var safeAreaRect: NSRect { bounds }
|
||||
override var safeAreaLayoutGuide: NSLayoutGuide { zeroSafeAreaLayoutGuide }
|
||||
|
||||
required init(rootView: Content) {
|
||||
super.init(rootView: rootView)
|
||||
addLayoutGuide(zeroSafeAreaLayoutGuide)
|
||||
NSLayoutConstraint.activate([
|
||||
zeroSafeAreaLayoutGuide.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
zeroSafeAreaLayoutGuide.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
zeroSafeAreaLayoutGuide.topAnchor.constraint(equalTo: topAnchor),
|
||||
zeroSafeAreaLayoutGuide.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
private enum CmuxThemeNotifications {
|
||||
static let reloadConfig = Notification.Name("com.cmuxterm.themes.reload-config")
|
||||
}
|
||||
|
|
@ -5457,7 +5481,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
} else {
|
||||
window.center()
|
||||
}
|
||||
window.contentView = NSHostingView(rootView: root)
|
||||
window.contentView = MainWindowHostingView(rootView: root)
|
||||
|
||||
// Apply shared window styling.
|
||||
attachUpdateAccessory(to: window)
|
||||
|
|
|
|||
|
|
@ -1173,6 +1173,7 @@ private struct NotificationPopoverRow: View {
|
|||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class UpdateTitlebarAccessoryController {
|
||||
private weak var updateViewModel: UpdateViewModel?
|
||||
private var didStart = false
|
||||
|
|
@ -1213,7 +1214,9 @@ final class UpdateTitlebarAccessoryController {
|
|||
queue: .main
|
||||
) { [weak self] notification in
|
||||
guard let window = notification.object as? NSWindow else { return }
|
||||
self?.attachIfNeeded(to: window)
|
||||
Task { @MainActor [weak self] in
|
||||
self?.attachIfNeeded(to: window)
|
||||
}
|
||||
})
|
||||
|
||||
observers.append(center.addObserver(
|
||||
|
|
@ -1222,7 +1225,9 @@ final class UpdateTitlebarAccessoryController {
|
|||
queue: .main
|
||||
) { [weak self] notification in
|
||||
guard let window = notification.object as? NSWindow else { return }
|
||||
self?.attachIfNeeded(to: window)
|
||||
Task { @MainActor [weak self] in
|
||||
self?.attachIfNeeded(to: window)
|
||||
}
|
||||
})
|
||||
|
||||
// We intentionally do not rely on "window became visible" notifications here:
|
||||
|
|
@ -1242,7 +1247,9 @@ final class UpdateTitlebarAccessoryController {
|
|||
let delays: [TimeInterval] = [0.05, 0.15, 0.3, 0.6, 1.0, 2.0, 3.0]
|
||||
for delay in delays {
|
||||
let item = DispatchWorkItem { [weak self] in
|
||||
self?.attachToExistingWindows()
|
||||
Task { @MainActor [weak self] in
|
||||
self?.attachToExistingWindows()
|
||||
}
|
||||
#if DEBUG
|
||||
let env = ProcessInfo.processInfo.environment
|
||||
if env["CMUX_UI_TEST_MODE"] == "1" {
|
||||
|
|
@ -1258,7 +1265,6 @@ final class UpdateTitlebarAccessoryController {
|
|||
}
|
||||
|
||||
private func attachIfNeeded(to window: NSWindow) {
|
||||
guard !attachedWindows.contains(window) else { return }
|
||||
guard !isSettingsWindow(window) else { return }
|
||||
|
||||
// Window identifiers are assigned by SwiftUI via WindowAccessor, which can run
|
||||
|
|
@ -1270,8 +1276,10 @@ final class UpdateTitlebarAccessoryController {
|
|||
if attempts < 40 {
|
||||
pendingAttachRetries[key] = attempts + 1
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self, weak window] in
|
||||
guard let self, let window else { return }
|
||||
self.attachIfNeeded(to: window)
|
||||
Task { @MainActor [weak self, weak window] in
|
||||
guard let self, let window else { return }
|
||||
self.attachIfNeeded(to: window)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pendingAttachRetries.removeValue(forKey: key)
|
||||
|
|
@ -1281,6 +1289,13 @@ final class UpdateTitlebarAccessoryController {
|
|||
|
||||
pendingAttachRetries.removeValue(forKey: ObjectIdentifier(window))
|
||||
|
||||
guard WorkspaceTitlebarSettings.isVisible() else {
|
||||
removeAccessoryIfPresent(from: window)
|
||||
return
|
||||
}
|
||||
|
||||
guard !attachedWindows.contains(window) else { return }
|
||||
|
||||
if !window.titlebarAccessoryViewControllers.contains(where: { $0.view.identifier == controlsIdentifier }) {
|
||||
let controls = TitlebarControlsAccessoryViewController(
|
||||
notificationStore: TerminalNotificationStore.shared
|
||||
|
|
@ -1302,6 +1317,40 @@ final class UpdateTitlebarAccessoryController {
|
|||
#endif
|
||||
}
|
||||
|
||||
private func removeAccessoryIfPresent(from window: NSWindow) {
|
||||
let matchingIndices = window.titlebarAccessoryViewControllers.indices.reversed().filter { index in
|
||||
window.titlebarAccessoryViewControllers[index].view.identifier == controlsIdentifier
|
||||
}
|
||||
guard !matchingIndices.isEmpty || attachedWindows.contains(window) else { return }
|
||||
|
||||
for index in matchingIndices {
|
||||
let accessory = window.titlebarAccessoryViewControllers[index]
|
||||
if let controls = accessory as? TitlebarControlsAccessoryViewController {
|
||||
controls.dismissNotificationsPopover()
|
||||
}
|
||||
window.removeTitlebarAccessoryViewController(at: index)
|
||||
}
|
||||
|
||||
attachedWindows.remove(window)
|
||||
pendingAttachRetries.removeValue(forKey: ObjectIdentifier(window))
|
||||
DispatchQueue.main.async { [weak window] in
|
||||
guard let window else { return }
|
||||
window.contentView?.needsLayout = true
|
||||
window.contentView?.superview?.needsLayout = true
|
||||
window.contentView?.layoutSubtreeIfNeeded()
|
||||
window.contentView?.superview?.layoutSubtreeIfNeeded()
|
||||
window.invalidateShadow()
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
let env = ProcessInfo.processInfo.environment
|
||||
if env["CMUX_UI_TEST_MODE"] == "1" {
|
||||
let ident = window.identifier?.rawValue ?? "<nil>"
|
||||
UpdateLogStore.shared.append("removed titlebar accessories from window id=\(ident)")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func isSettingsWindow(_ window: NSWindow) -> Bool {
|
||||
if window.identifier?.rawValue == "cmux.settings" {
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -3241,6 +3241,16 @@ struct SettingsView: View {
|
|||
)
|
||||
}
|
||||
|
||||
private var showWorkspaceTitlebarBinding: Binding<Bool> {
|
||||
Binding(
|
||||
get: { showWorkspaceTitlebar },
|
||||
set: { newValue in
|
||||
showWorkspaceTitlebar = newValue
|
||||
reassertSettingsWindowFocusIfNeeded()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private var settingsSidebarTintLightBinding: Binding<Color> {
|
||||
Binding(
|
||||
get: {
|
||||
|
|
@ -3590,9 +3600,10 @@ struct SettingsView: View {
|
|||
String(localized: "settings.app.showWorkspaceTitlebar", defaultValue: "Show Workspace Title Bar"),
|
||||
subtitle: workspaceTitlebarSubtitle
|
||||
) {
|
||||
Toggle("", isOn: $showWorkspaceTitlebar)
|
||||
Toggle("", isOn: showWorkspaceTitlebarBinding)
|
||||
.labelsHidden()
|
||||
.controlSize(.small)
|
||||
.accessibilityIdentifier("SettingsShowWorkspaceTitlebarToggle")
|
||||
.accessibilityLabel(
|
||||
String(localized: "settings.app.showWorkspaceTitlebar", defaultValue: "Show Workspace Title Bar")
|
||||
)
|
||||
|
|
@ -4625,6 +4636,19 @@ struct SettingsView: View {
|
|||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
|
||||
private func reassertSettingsWindowFocusIfNeeded() {
|
||||
DispatchQueue.main.async {
|
||||
guard let window = SettingsWindowController.shared.window, window.isVisible else { return }
|
||||
window.orderFrontRegardless()
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
DispatchQueue.main.async {
|
||||
guard window.isVisible else { return }
|
||||
window.orderFrontRegardless()
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func resetAllSettings() {
|
||||
isResettingSettings = true
|
||||
appLanguage = LanguageSettings.defaultLanguage.rawValue
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue