Mediensteuerung hochkant, Symbole wieder sichtbar, „Auto" als Zustand

Die Mediensteuerung stand nebeneinander: Cover links, alles andere in den
verbleibenden gut hundert Punkten. Daher „Bernhard H…" statt eines Titels.
Jetzt übereinander, wie es zu einer hochkanten Kachel passt.

Das Cover ragte zusätzlich über die Kachel hinaus, weil `clipShape` vor dem
Rahmen stand — beschnitten wurde damit auf die Eigengröße des Bildes, und
die ist bei einem Podcast-Cover ein Vielfaches. Erst Rahmen, dann
beschneiden.

Menüleistensymbole waren kaum zu sehen. `color.set()` vor `image.draw(in:)`
färbt ein Template-Bild nicht: es wird schwarz gezeichnet, auf einer dunklen
Menüleiste also unsichtbar. AppKit färbt Template-Bilder nur ein, wenn sie
als `image` eines Buttons gesetzt sind — in einer selbst gezeichneten Ansicht
muss die Farbe in die Symbolkonfiguration. Betraf alle fünf Zeichenstellen,
nicht nur die Lüfter.

„fans.auto" stand als roher Schlüssel da: Xcode hatte ihn extrahiert, gefüllt
wurde er nie. Beim Nachsehen waren es 35 solche Einträge über alle Kataloge
hinweg — die meisten reine Formatschlüssel, die als Durchreicher trotzdem
einen Wert brauchen.

Der Automatik-Knopf ist jetzt eine gefüllte Pille, wenn sie greift. Blauer
Text sieht aus wie ein Link und nicht wie ein Zustand.

Und ein Absturz, der noch niemandem passiert ist: meldet der Helfer für einen
stehenden Lüfter keine Grenzen, hätte `Slider(in: 0...0)` die App beendet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-11 11:12:33 +02:00
parent 93fe95827f
commit b22a44ba70
14 changed files with 1400 additions and 1027 deletions

View File

@@ -56,45 +56,51 @@ private struct Playing: View {
let state: NowPlaying
var body: some View {
HStack(spacing: 10) {
// Übereinander, nicht nebeneinander. Die Kachel ist hochkant: neben
// einem Cover blieben für den Titel noch gut hundert Punkte, und dann
// steht dort Bernhard H" statt eines Titels.
VStack(alignment: .leading, spacing: 7) {
Artwork(data: state.artwork)
.frame(width: artworkSize, height: artworkSize)
.frame(maxWidth: .infinity)
.frame(height: 88)
VStack(alignment: .leading, spacing: 2) {
VStack(alignment: .leading, spacing: 1) {
Text(state.title)
.font(Onyx.Font.title)
.font(Onyx.Font.caption.weight(.medium))
.foregroundStyle(Onyx.Color.textPrimary)
.lineLimit(1)
.lineLimit(2)
Text(state.artist)
.font(Onyx.Font.caption)
.foregroundStyle(Onyx.Color.textSecondary)
.lineLimit(1)
}
.frame(maxWidth: .infinity, alignment: .leading)
Spacer(minLength: 2)
Spacer(minLength: 0)
Scrubber(model: model, state: state)
Scrubber(model: model, state: state)
HStack {
TransportControls(model: model, size: 15)
Spacer(minLength: 0)
if state.origin == .appleScript {
// Sichtbar machen, dass gerade die schwächere Quelle
// greift: dann fehlen Cover und Browser-Wiedergaben,
// und das soll erklärbar sein statt rätselhaft.
Image(systemName: "applescript")
.font(.system(size: 9))
.foregroundStyle(Onyx.Color.textTertiary)
.help(Text("widget.media.viaAppleScript", bundle: .module))
}
HStack(spacing: 0) {
Spacer(minLength: 0)
TransportControls(model: model, size: 15)
Spacer(minLength: 0)
}
.overlay(alignment: .trailing) {
if state.origin == .appleScript {
// Sichtbar machen, dass gerade die schwächere Quelle
// greift: dann fehlen Cover und Browser-Wiedergaben,
// und das soll erklärbar sein statt rätselhaft.
Image(systemName: "applescript")
.font(.system(size: 9))
.foregroundStyle(Onyx.Color.textTertiary)
.help(Text("widget.media.viaAppleScript", bundle: .module))
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.contentShape(Rectangle())
.onTapGesture(count: 2) { model.activateSource() }
}
private let artworkSize: CGFloat = 56
}
private struct Artwork: View {
@@ -103,6 +109,9 @@ private struct Artwork: View {
var body: some View {
let shape = RoundedRectangle(cornerRadius: 8, style: .continuous)
// Reihenfolge zählt: erst der Rahmen, dann das Beschneiden. Andersherum
// beschneidet man das Bild auf seine eigene, viel größere Größe und es
// ragt anschließend über die Kachel hinaus, mitten in den Nachbarn.
Group {
if let data, let image = NSImage(data: data) {
Image(nsImage: image).resizable().aspectRatio(contentMode: .fill)
@@ -118,6 +127,7 @@ private struct Artwork: View {
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.clipShape(shape)
.overlay(shape.strokeBorder(Onyx.Color.hairline, lineWidth: 1))
}