Files
onyx/Onyx/AppModel.swift
Guido Schmit c0874c83b7 Berechtigungen zeigen ihren Zustand live, Dock-Symbol wird wählbar
Die Einstellungen zeigten weiter "Anfragen", obwohl die Ortung längst lief:
authorizationStatus ist eine Abfrage, keine Beobachtung, und ich hatte sie
einmalig beim Öffnen gelesen. Jetzt meldet locationManagerDidChangeAuthorization
die Änderung, WeatherModel führt daraus einen beobachtbaren Wert, und die
Einstellungen aktualisieren sich, während das Fenster offen steht. Nach der
Zustimmung wird sofort geladen statt beim nächsten Anlauf.

Der Knopf "Systemeinstellungen öffnen" ganz unten zeigte fest auf den
Kalenderbereich — wer ihn beim Standort drückte, landete am falschen Ort. Es
gibt ihn nicht mehr; stattdessen führt jede Berechtigung in ihren eigenen
Bereich, und der Knopf erscheint nur im Zustand "abgelehnt". Bei "noch nie
gefragt" steht dort Anfragen, bei "erteilt" gar nichts: drei Zustände, drei
Antworten. Genau daran ist die erste Fassung gescheitert.

Dock-Symbol ist jetzt eine Einstellung, standardmäßig aus. Onyx läuft als
LSUIElement — das war Absicht und keine Auslassung: es hat kein Hauptfenster
und wäre im Dock ein Symbol, das beim Anklicken nichts öffnet, und es stünde im
Programmumschalter zwischen den Programmen, mit denen man arbeitet. Wer es
trotzdem will, schaltet es ein; die Wahl wird beim Start wieder angewendet.
2026-08-10 21:24:49 +02:00

116 lines
4.0 KiB
Swift

import SwiftUI
import AppKit
import OnyxNotch
import OnyxWidgetKit
/// Der geteilte Zustand von Onyx: was im Panel liegt und auf welchen Displays.
///
/// Änderungen wirken sofort. Eine Einstellung, die erst nach einem Neustart
/// greift, ist in einer App ohne Fenster praktisch unauffindbar man sieht ja
/// nicht, dass etwas passiert ist.
@MainActor
@Observable
final class AppModel {
private let store: LayoutStore
private let defaults: UserDefaults
/// Wird gerufen, wenn sich die Panelgröße geändert haben könnte.
var onLayoutChanged: (([WidgetPlacement]) -> Void)?
/// Wird gerufen, wenn Panels auf anderen Displays erscheinen sollen.
var onDisplayPolicyChanged: ((DisplayPolicy) -> Void)?
var layout: [WidgetPlacement] {
didSet {
guard layout != oldValue else { return }
persistLayout()
onLayoutChanged?(layout)
}
}
var displayPolicy: DisplayPolicy {
didSet {
guard displayPolicy != oldValue else { return }
defaults.set(displayPolicy.rawValue, forKey: Keys.displayPolicy)
onDisplayPolicyChanged?(displayPolicy)
}
}
var showsMenuBarIcon: Bool {
didSet { defaults.set(showsMenuBarIcon, forKey: Keys.showsMenuBarIcon) }
}
/// Ob Onyx im Dock auftaucht.
///
/// Standardmäßig nicht: Onyx ist ein Werkzeug, das in der Notch und in der
/// Menüleiste lebt, hat kein Hauptfenster und wäre im Dock ein Symbol, das
/// beim Anklicken nichts öffnet. Es taucht dann auch im Programmumschalter
/// auf und steht dort zwischen den Programmen, mit denen man arbeitet.
var showsDockIcon: Bool {
didSet {
guard showsDockIcon != oldValue else { return }
defaults.set(showsDockIcon, forKey: Keys.showsDockIcon)
NSApp.setActivationPolicy(showsDockIcon ? .regular : .accessory)
}
}
private enum Keys {
static let displayPolicy = "onyx.displayPolicy"
static let showsMenuBarIcon = "onyx.showsMenuBarIcon"
static let showsDockIcon = "onyx.showsDockIcon"
}
init(store: LayoutStore = .standard(), defaults: UserDefaults = .standard) {
self.store = store
self.defaults = defaults
let known = Set(WidgetRegistry.shared.all.map(\.id))
self.layout = store.load(knownWidgetIDs: known)
self.displayPolicy = defaults.string(forKey: Keys.displayPolicy)
.flatMap(DisplayPolicy.init(rawValue:)) ?? .builtInOnly
// Standardmäßig sichtbar: das Menüleistensymbol ist der einzige Weg
// zurück, wenn die Notch einmal nicht reagiert.
self.showsMenuBarIcon = defaults.object(forKey: Keys.showsMenuBarIcon) as? Bool ?? true
self.showsDockIcon = defaults.object(forKey: Keys.showsDockIcon) as? Bool ?? false
}
// MARK: - Widgets
var panelContentSize: CGSize { WidgetGrid.panelSize(for: layout) }
func isInPanel(_ widgetID: String) -> Bool {
layout.contains { $0.widgetID == widgetID }
}
func add(_ widget: any OnyxWidget) {
guard !isInPanel(widget.id) else { return }
let size = widget.supportedSizes.first ?? .small
layout.append(WidgetPlacement(widgetID: widget.id, size: size))
}
func remove(_ widgetID: String) {
layout.removeAll { $0.widgetID == widgetID }
}
func setSize(_ size: WidgetSize, for widgetID: String) {
guard let index = layout.firstIndex(where: { $0.widgetID == widgetID }) else { return }
layout[index].size = size
}
func move(fromOffsets source: IndexSet, toOffset destination: Int) {
layout.move(fromOffsets: source, toOffset: destination)
}
private func persistLayout() {
do {
try store.save(layout)
} catch {
// Ein fehlgeschlagener Schreibvorgang darf die App nicht anhalten;
// beim nächsten Start greift dann das Standardlayout.
NSLog("Onyx: Layout konnte nicht gespeichert werden — \(error.localizedDescription)")
}
}
}