Das Grün, das überall auftauchte, war die System-Akzentfarbe: `.tint` und `.accentColor` werden ohne eigene Angabe dorthin durchgereicht, und das betraf alle Popovers und das ganze Einstellungsfenster. Die Notch-Widgets dagegen benutzten Onyx' eigenes Blau. Zwei Farben, ungewollt. Jetzt eine, und die ist einstellbar. Ein neuer Reiter „Farben": Akzentfarbe mit sechs Vorschlägen und einem Knopf, der die Systemfarbe übernimmt, dazu das Paar für Herunter- und Hochladen samt Tauschknopf. Die Signalfarben bleiben fest und stehen nur zur Ansicht dort. Gelb heißt Warnung, Rot heißt kritisch, Grün heißt in Ordnung — bei Temperatur, Akku, Lüfter und Fehlern. Wer sie umstellen kann, kann ihre Bedeutung zerstören; ein rotes „alles gut" liest niemand richtig. Sichtbar sind sie trotzdem, sonst sucht man die Warnfarbe im Reiter und findet sie nirgends. Umgesetzt über die Tokens statt über vierzig Fundstellen: `Onyx.Color.accent` ist keine Konstante mehr, sondern liest bei jedem Zugriff aus `OnyxTheme`. Damit merkt SwiftUI die Abhängigkeit und zeichnet neu, sobald die Farbe sich ändert. Dazu ein `.tint` an den vier Wurzeln — Panel, Popovers, Einstellungen, Einrichtung —, damit auch Haken, Regler und Auswahlfelder mitziehen. Nebenbei die Beschriftung: „Je Programm" heißt jetzt „Programme". Großgesetzt las sich das als englisches „J-E". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
132 lines
4.6 KiB
Swift
132 lines
4.6 KiB
Swift
import SwiftUI
|
|
import OnyxDesign
|
|
|
|
/// Die Farbwahl.
|
|
///
|
|
/// Drei Farben, nicht zehn: die Akzentfarbe, mit der Onyx sich selbst zeichnet,
|
|
/// und das Paar für Herunter- und Hochladen. Die Signalfarben stehen bewusst
|
|
/// nicht hier — Gelb heißt Warnung, Rot heißt kritisch, und wer das umstellen
|
|
/// kann, kann die Bedeutung zerstören.
|
|
struct ColorSettingsView: View {
|
|
|
|
@Bindable var theme: OnyxTheme
|
|
|
|
/// Ein paar Vorschläge, damit man nicht im Farbwähler anfangen muss.
|
|
///
|
|
/// Alle gedämpft: Onyx ist eine dunkle Fläche, und eine volle Sättigung
|
|
/// darauf sticht, statt zu leiten.
|
|
private static let suggestions: [(name: LocalizedStringKey, hex: String)] = [
|
|
("color.suggestion.onyx", "#7FA8FF"),
|
|
("color.suggestion.mint", "#6FCF97"),
|
|
("color.suggestion.amber", "#E8B44A"),
|
|
("color.suggestion.coral", "#E5675C"),
|
|
("color.suggestion.lilac", "#B79CFF"),
|
|
("color.suggestion.slate", "#9AA6B2"),
|
|
]
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section("color.section.accent") {
|
|
ColorPicker(selection: $theme.accent, supportsOpacity: false) {
|
|
Text("color.accent")
|
|
}
|
|
|
|
HStack(spacing: 8) {
|
|
ForEach(Self.suggestions, id: \.hex) { suggestion in
|
|
Swatch(hex: suggestion.hex,
|
|
name: suggestion.name,
|
|
isSelected: theme.accent.onyxHex == suggestion.hex) {
|
|
theme.accent = Color(onyxHex: suggestion.hex) ?? theme.accent
|
|
}
|
|
}
|
|
Spacer(minLength: 0)
|
|
}
|
|
|
|
Button { theme.adoptSystemAccent() } label: {
|
|
Text("color.adoptSystem")
|
|
}
|
|
Text("color.accent.hint")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Section("color.section.network") {
|
|
ColorPicker(selection: $theme.download, supportsOpacity: false) {
|
|
Text("color.download")
|
|
}
|
|
ColorPicker(selection: $theme.upload, supportsOpacity: false) {
|
|
Text("color.upload")
|
|
}
|
|
Button {
|
|
let previous = theme.download
|
|
theme.download = theme.upload
|
|
theme.upload = previous
|
|
} label: {
|
|
Text("color.swap")
|
|
}
|
|
Text("color.network.hint")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Section("color.section.fixed") {
|
|
// Sichtbar, aber nicht bedienbar: sonst sucht man die
|
|
// Warnfarbe hier und findet sie nirgends.
|
|
FixedRow(color: Onyx.Color.positive, label: "color.fixed.positive")
|
|
FixedRow(color: Onyx.Color.warning, label: "color.fixed.warning")
|
|
FixedRow(color: Onyx.Color.critical, label: "color.fixed.critical")
|
|
Text("color.fixed.hint")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Section {
|
|
Button { theme.reset() } label: {
|
|
Text("color.reset")
|
|
}
|
|
.disabled(theme.isDefault)
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
}
|
|
}
|
|
|
|
/// Ein Farbfeld zum Anklicken.
|
|
private struct Swatch: View {
|
|
let hex: String
|
|
let name: LocalizedStringKey
|
|
let isSelected: Bool
|
|
let action: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
Circle()
|
|
.fill(Color(onyxHex: hex) ?? .gray)
|
|
.frame(width: 20, height: 20)
|
|
.overlay {
|
|
// Der Ring statt eines Hakens: ein Haken auf einer dunklen
|
|
// Farbe ist nicht zu sehen, auf einer hellen nicht auf
|
|
// einer dunklen Fläche.
|
|
Circle()
|
|
.strokeBorder(.primary, lineWidth: isSelected ? 2 : 0)
|
|
.padding(-3)
|
|
}
|
|
}
|
|
.buttonStyle(.plain)
|
|
.help(Text(name))
|
|
}
|
|
}
|
|
|
|
private struct FixedRow: View {
|
|
let color: Color
|
|
let label: LocalizedStringKey
|
|
|
|
var body: some View {
|
|
HStack {
|
|
Text(label)
|
|
Spacer(minLength: 12)
|
|
Circle().fill(color).frame(width: 14, height: 14)
|
|
}
|
|
}
|
|
}
|