Akku wird gezeichnet wie der von macOS

Der Blitz stand neben dem Akku statt darin, und das sah nach zwei Dingen
aus statt nach einem. Mit SF Symbols ging es nicht anders: fünf feste
Füllstufen, und Blitz-Varianten gibt es nur bei hundert Prozent.

Also selbst gezeichnet — ein Rahmen, ein Knubbel, eine Füllung, ein Blitz.
Das ist am Ende die kleinere Lösung als die Symbolakrobatik und gibt
obendrein, was vorher fehlte: die Füllung läuft stufenlos, 60 % sieht aus
wie 60 % und nicht wie 50 %.

Der Blitz wird ausgestanzt, nicht daraufgelegt. `destinationOut` nimmt weg,
was vorher gezeichnet wurde; übrig bleibt ein Loch in Blitzform, durch das
die Menüleiste scheint. Das funktioniert auf hellem wie dunklem Grund, ohne
die Hintergrundfarbe zu kennen — und genau so sieht der von macOS aus.

Unter zwanzig Prozent wird die Füllung rot, beim Laden nie: eine Warnung
vor etwas, das sich schon erledigt, ist keine.

Dieselbe Form auch in der Akku-Kachel. Zwei verschiedene Akkus in einer App
wären schlechter als gar keiner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-11 15:49:04 +02:00
parent 4b5e86b3f4
commit 9968236a37
4 changed files with 261 additions and 36 deletions

View File

@@ -0,0 +1,63 @@
import Testing
import CoreGraphics
@testable import MetricsProvider
// SF Symbols kennt fünf Füllstufen und keine Blitz-Varianten außer bei 100 %.
// Beides passt nicht zu dem, was macOS selbst zeigt: eine stufenlose Füllung
// und einen Blitz **im** Akku. Also selbst gezeichnet.
@Suite("Akkuglyph")
struct BatteryGlyphTests {
private let inner: CGFloat = 20
@Test("Die Füllung folgt dem Ladestand stufenlos")
func fillIsProportional() {
// Der eigentliche Gewinn gegenüber fünf festen Stufen: 60 % sieht aus
// wie 60 % und nicht wie 50 %.
#expect(BatteryGlyph.fillWidth(charge: 0.5, inner: inner) == 10)
#expect(BatteryGlyph.fillWidth(charge: 1.0, inner: inner) == 20)
}
@Test("Ein leerer Akku bleibt leer")
func emptyStaysEmpty() {
#expect(BatteryGlyph.fillWidth(charge: 0, inner: inner) == 0)
}
@Test("Ein fast leerer Akku ist trotzdem zu sehen")
func nearlyEmptyIsStillVisible() {
// Zwei Prozent ergäben 0,4 Punkte unsichtbar, und damit
// ununterscheidbar von leer". Ein Rest bleibt stehen.
let width = BatteryGlyph.fillWidth(charge: 0.02, inner: inner)
#expect(width >= BatteryGlyph.minimumFill)
}
@Test("Über hundert Prozent läuft nichts über")
func neverOverflows() {
#expect(BatteryGlyph.fillWidth(charge: 2, inner: inner) == inner)
#expect(BatteryGlyph.fillWidth(charge: -1, inner: inner) == 0)
}
@Test("Wenig Ladung gilt als kritisch")
func lowIsFlagged() {
// Dieselbe Schwelle wie in macOS: ab zwanzig Prozent abwärts wird die
// Farbe zum Signal.
#expect(BatteryGlyph.isLow(charge: 0.19))
#expect(!BatteryGlyph.isLow(charge: 0.21))
}
@Test("Beim Laden gilt nichts als kritisch")
func chargingIsNeverLow() {
// Ein roter Akku, der gerade lädt, wäre eine Warnung vor etwas, das
// sich schon erledigt.
#expect(!BatteryGlyph.isLow(charge: 0.05, isCharging: true))
}
@Test("Das Glyph hat die Maße der Menüleiste")
func sizeFitsMenuBar() {
// Breiter als hoch, wie ein Akku aussieht und niedrig genug, dass es
// in die 22 Punkte der Leiste passt.
#expect(BatteryGlyph.size.width > BatteryGlyph.size.height)
#expect(BatteryGlyph.size.height <= 14)
}
}