Files
onyx/Onyx/OnboardingWindowController.swift
Scarriffle 453353de58 Notch öffnet zuverlässig, Einstellungen beenden Onyx nicht mehr
Das Öffnen hing allein an Mausereignissen. Der Auslösebereich liegt aber in
der Menüleiste, und dorthin stellt macOS Bewegungen nicht verlässlich an
fremde Programme zu — mal kommen sie an, mal nicht, je nachdem, was gerade
den Zeiger führt. Ein Verfahren, das eine Zustellung voraussetzt, die es
nicht gibt, kann nicht zuverlässig sein.

Die Zeigerposition wird deshalb jetzt immer abgefragt, im Ruhezustand
zehnmal je Sekunde, bei offenem Panel sechzigmal. Die Monitore sind nur noch
Beschleunigung. Kosten: 0,1 % CPU im Leerlauf, gemessen. Nachgeprüft mit
fünf Anläufen aus verschiedenen Richtungen — fünfmal offen.

Die Unterdrückung war zu grob: jedes Vollbildfenster galt als Grund, das
Panel wegzuhalten. Damit war die Notch tot, sobald Xcode im Vollbild lief.
Gemeint war immer nur der Film über den ganzen Bildschirm, und den erkennt
man daran, dass etwas den Bildschirm wachhält — ein Textfenster tut das
nicht. Beide Bedingungen zusammen, nicht eine davon.

Dazu ein Loch, das dabei auffiel: fiel die Unterdrückung weg, während der
Zeiger schon in der Notch stand, wartete die Maschine auf ein Ereignis, das
erst bei der nächsten Bewegung kam.

Der rote Knopf blendet Fenster jetzt aus, statt sie zu schließen. Onyx hat
kein Hauptfenster; „Fenster zu" heißt hier nie „fertig". Manche
Hilfsprogramme erzwingen aber genau diese Gleichung und beenden eine App,
sobald ihr letztes Fenster verschwindet — dann war Onyx nach einem Blick in
die Einstellungen weg, samt Menüleiste und Notch. Ein Fenster, das nie
wirklich schließt, gibt dafür keinen Anlass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:21:00 +02:00

87 lines
3.2 KiB
Swift

import AppKit
import SwiftUI
import CalendarProvider
import WeatherProvider
import AudioProvider
/// Zeigt das Onboarding einmal, beim ersten Start.
///
/// Als eigenes Fenster und nicht im Panel: TCC blendet Berechtigungsdialoge
/// stillschweigend aus, wenn keine App im Vordergrund steht, und eine
/// `.accessory`-App mit ausgefahrenem Panel steht nicht im Vordergrund. Genau
/// daran ist die Kalenderberechtigung schon einmal gescheitert.
@MainActor
final class OnboardingWindowController: NSObject, NSWindowDelegate {
private static let doneKey = "onboarding.completed"
private var window: NSWindow?
private let defaults: UserDefaults
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
}
var isDone: Bool { defaults.bool(forKey: Self.doneKey) }
func showIfNeeded(calendarModel: CalendarModel, weatherModel: WeatherModel,
mixer: AudioMixer, fanControl: FanControl,
launchAtLogin: LaunchAtLogin) {
guard !isDone else { return }
show(calendarModel: calendarModel, weatherModel: weatherModel, mixer: mixer,
fanControl: fanControl, launchAtLogin: launchAtLogin)
}
func show(calendarModel: CalendarModel, weatherModel: WeatherModel,
mixer: AudioMixer, fanControl: FanControl, launchAtLogin: LaunchAtLogin) {
// Solange dieses Fenster steht, gehört Onyx in den Dock und in den
// Vordergrund sonst erscheinen die Systemdialoge gar nicht.
NSApp.setActivationPolicy(.regular)
if window == nil {
let hosting = NSHostingController(
rootView: OnboardingView(
calendarModel: calendarModel, weatherModel: weatherModel,
mixer: mixer, fanControl: fanControl, launchAtLogin: launchAtLogin,
onFinish: { [weak self] in self?.finish() }))
let window = NSWindow(contentViewController: hosting)
window.title = "Onyx"
window.styleMask = [.titled, .closable]
window.isReleasedWhenClosed = false
window.delegate = self
window.center()
self.window = window
}
NSApp.activate(ignoringOtherApps: true)
window?.makeKeyAndOrderFront(nil)
}
/// Auch das Schließen über den roten Knopf gilt als erledigt. Ein Fenster,
/// das beim nächsten Start wiederkommt, weil man es weggeklickt hat, ist
/// eine Zumutung.
private func finish() {
defaults.set(true, forKey: Self.doneKey)
hide()
}
/// Wie bei den Einstellungen: ausblenden statt schließen, damit ein
/// Hilfsprogramm mit letztes Fenster zu = App beenden" Onyx nicht mit
/// wegräumt.
func windowShouldClose(_ sender: NSWindow) -> Bool {
defaults.set(true, forKey: Self.doneKey)
hide()
return false
}
private func hide() {
window?.orderOut(nil)
// Zurück in den Hintergrund es sei denn, ein anderes Fenster steht
// noch offen.
if !NSApp.windows.contains(where: { $0 !== window && $0.isVisible }) {
NSApp.setActivationPolicy(.accessory)
}
}
}