Files
OrionToolbar/patches/oriontoolbar_settings_window_fix.py
T
2026-10-23 09:54:58 +01:00

108 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Replace fragile MenuBarExtra Settings sheet with a standalone modal NSWindow."""
from pathlib import Path
from datetime import datetime
import shutil
root = Path(__file__).resolve().parent.parent
if not (root / 'OrionToolbar/OrionMenuView.swift').exists():
root = Path.cwd()
path = root / 'OrionToolbar/OrionMenuView.swift'
if not path.is_file():
raise SystemExit(f'Missing {path}')
s = path.read_text()
if 'OrionSettingsWindowPresenter.present(vm: vm)' in s:
print('Already applied')
raise SystemExit(0)
start = s.find(' .sheet(isPresented: $settingsPresented) {')
if start < 0:
raise SystemExit('Expected Settings sheet not found. No changes made.')
# Match Swift brace depth for the .sheet closure.
brace = s.find('{', start)
depth = 0
end = None
for i in range(brace, len(s)):
if s[i] == '{': depth += 1
elif s[i] == '}':
depth -= 1
if depth == 0:
end = i + 1
break
if end is None:
raise SystemExit('Unbalanced Settings sheet. No changes made.')
s = s[:start] + s[end:]
s = s.replace(' @State private var settingsPresented = false\n', '', 1)
start = s.find(' private func showSettings() {')
if start < 0:
raise SystemExit('showSettings() not found. No changes made.')
brace = s.find('{', start)
depth = 0
end = None
for i in range(brace, len(s)):
if s[i] == '{': depth += 1
elif s[i] == '}':
depth -= 1
if depth == 0:
end = i + 1
break
if end is None:
raise SystemExit('Unbalanced showSettings(). No changes made.')
s = s[:start] + ''' private func showSettings() {
OrionSettingsWindowPresenter.present(vm: vm)
}''' + s[end:]
# Put window presentation in the existing file so no Xcode project membership changes are required.
s += '''
// A MenuBarExtra window is transient. Sheets attached to it can disappear
// when the user interacts with controls inside Settings. Use a real window.
@MainActor
private final class OrionSettingsWindowPresenter: NSObject, NSWindowDelegate {
private static var current: OrionSettingsWindowPresenter?
private var window: NSWindow?
static func present(vm: OrionVMController) {
if let current, let window = current.window {
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
return
}
let presenter = OrionSettingsWindowPresenter()
current = presenter
presenter.open(vm: vm)
}
private func open(vm: OrionVMController) {
let content = SettingsView()
.environmentObject(vm)
.preferredColorScheme(.dark)
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 650, height: 380),
styleMask: [.titled, .closable, .miniaturizable],
backing: .buffered,
defer: false
)
window.title = "OrionToolbar Settings"
window.contentViewController = NSHostingController(rootView: content)
window.delegate = self
window.isReleasedWhenClosed = false
window.level = .floating
window.center()
self.window = window
NSApp.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}
func windowWillClose(_ notification: Notification) {
window = nil
Self.current = nil
}
}
'''
backup = root / 'backups' / datetime.now().strftime('%Y%m%d-%H%M%S-%f') / 'OrionToolbar' / path.name
backup.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(path, backup)
path.write_text(s)
print('Updated:', path)
print('Backup:', backup)
print('Settings now uses a persistent foreground window.')