Menüleiste zeigt das Glyph, nicht das ganze App-Icon

Das vollständige App-Icon bringt die abgerundete Kachel samt Farbverlauf mit.
Die gehört in Dock und Finder; in der Menüleiste steht sie als farbiger Klotz
zwischen lauter Strichsymbolen.

Geladen wird jetzt dieselbe SVG-Datei, aus der auch das App-Icon gebaut wird.
NSImage liest sie als Vektor (_NSSVGImageRep), sie skaliert also verlustfrei
auf die 18 pt der Menüleiste.

Der Weg dorthin ist ein Kopierschritt und keine zweite Datei: actool kompiliert
das .icon-Bündel zu .icns und Assets.car und legt nichts Lesbares ins Bundle,
und eine einzelne Datei aus dem Bündel lässt sich nicht als Ressource
referenzieren — XcodeGen bricht dabei ab. Ein Build-Schritt kopiert sie
stattdessen, damit es bei einer Quelldatei bleibt.

Weiterhin kein Template-Bild: als Schablone bliebe vom Kristall nur eine
schwarze Silhouette.
This commit is contained in:
Guido Schmit
2026-08-10 20:55:40 +02:00
parent 4b559ba840
commit 03dd7b945e
4 changed files with 385 additions and 332 deletions

View File

@@ -114,6 +114,7 @@
6B11A5AC704B6429B9484789 /* Sources */, 6B11A5AC704B6429B9484789 /* Sources */,
B211CE3C0EF94A8D680E4AE0 /* Resources */, B211CE3C0EF94A8D680E4AE0 /* Resources */,
7FF994FE5530D53BEFBE13FE /* Frameworks */, 7FF994FE5530D53BEFBE13FE /* Frameworks */,
F10EA44D021D8D6C50BEDCAA /* Glyph für die Menüleiste ins Bundle kopieren */,
); );
buildRules = ( buildRules = (
); );
@@ -183,6 +184,29 @@
}; };
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F10EA44D021D8D6C50BEDCAA /* Glyph für die Menüleiste ins Bundle kopieren */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"$(SRCROOT)/Onyx/AppIcon.icon/Assets/quartz.svg",
);
name = "Glyph für die Menüleiste ins Bundle kopieren";
outputFileListPaths = (
);
outputPaths = (
"$(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/quartz.svg",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "SRC=\"$SRCROOT/Onyx/AppIcon.icon/Assets/quartz.svg\"\nDST=\"$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/quartz.svg\"\nif [ -f \"$SRC\" ]; then\n mkdir -p \"$(dirname \"$DST\")\"\n cp \"$SRC\" \"$DST\"\nelse\n echo \"warning: $SRC fehlt — Menüleiste fällt auf ein SF-Symbol zurück\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
6B11A5AC704B6429B9484789 /* Sources */ = { 6B11A5AC704B6429B9484789 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;

View File

@@ -200,6 +200,39 @@
} }
} }
}, },
"settings.permissions.location" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Standort"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Location"
}
}
}
},
"settings.permissions.location.why" : {
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Onyx zeigt das Wetter an deinem aktuellen Standort. Ohne Zugriff kannst du stattdessen einen festen Ort wählen."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Onyx shows the weather for your current location. Without access you can pick a fixed place instead."
}
}
}
},
"settings.permissions.openSystem" : { "settings.permissions.openSystem" : {
"extractionState" : "stale", "extractionState" : "stale",
"localizations" : { "localizations" : {
@@ -452,38 +485,6 @@
} }
} }
} }
},
"settings.permissions.location": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Standort"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Location"
}
}
}
},
"settings.permissions.location.why": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Onyx zeigt das Wetter an deinem aktuellen Standort. Ohne Zugriff kannst du stattdessen einen festen Ort wählen."
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Onyx shows the weather for your current location. Without access you can pick a fixed place instead."
}
}
}
} }
}, },
"version" : "1.0" "version" : "1.0"

View File

@@ -159,32 +159,40 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
onyxItem = item onyxItem = item
} }
/// Das App-Icon, auf Menüleistengröße gebracht. /// Das Glyph aus dem App-Icon, auf Menüleistengröße gebracht.
/// ///
/// Bewusst **kein** Template-Bild: als Schablone bliebe von dem Kristall nur /// Nicht das vollständige App-Icon: das bringt die abgerundete Kachel samt
/// eine schwarze Silhouette übrig, und der Verlauf das Erkennungsmerkmal /// Farbverlauf mit, und die gehört in Dock und Finder, nicht in die
/// des Icons wäre weg. Der Preis dafür ist, dass es sich nicht automatisch /// Menüleiste. Dort steht sie als farbiger Klotz zwischen lauter
/// an helle und dunkle Menüleisten anpasst. /// Strichsymbolen.
/// ///
/// 18 pt ist die übliche Höhe für Menüleistensymbole; die Leiste ist 22 pt /// Geladen wird dieselbe SVG-Datei, aus der auch das App-Icon gebaut wird
/// hoch und braucht oben und unten Luft. /// NSImage liest sie als Vektor (`_NSSVGImageRep`), sie skaliert also
/// verlustfrei. Bewusst **kein** Template-Bild: als Schablone bliebe vom
/// Kristall eine schwarze Silhouette. Der Preis ist, dass er sich nicht
/// automatisch an helle und dunkle Menüleisten anpasst.
///
/// 18 pt ist die übliche Höhe; die Leiste ist 22 pt hoch und braucht oben
/// und unten Luft.
private static func menuBarIcon() -> NSImage { private static func menuBarIcon() -> NSImage {
guard let source = NSApp.applicationIconImage else {
// Sollte nicht vorkommen, aber ein Menüleisteneintrag ohne Symbol
// wäre unsichtbar und die App damit unerreichbar.
return NSImage(systemSymbolName: "circle.hexagongrid.fill",
accessibilityDescription: "Onyx") ?? NSImage()
}
let size = NSSize(width: 18, height: 18) let size = NSSize(width: 18, height: 18)
if let url = Bundle.main.url(forResource: "quartz", withExtension: "svg"),
let glyph = NSImage(contentsOf: url) {
let image = NSImage(size: size, flipped: false) { rect in let image = NSImage(size: size, flipped: false) { rect in
source.draw(in: rect, from: .zero, operation: .sourceOver, fraction: 1) glyph.draw(in: rect, from: .zero, operation: .sourceOver, fraction: 1)
return true return true
} }
image.accessibilityDescription = "Onyx" image.accessibilityDescription = "Onyx"
return image return image
} }
// Ein Menüleisteneintrag ohne Bild wäre unsichtbar und die App darüber
// unerreichbar deshalb ein Rückfall statt eines leeren Knopfs.
return NSImage(systemSymbolName: "circle.hexagongrid.fill",
accessibilityDescription: "Onyx") ?? NSImage()
}
private func observeMenuBarPreference() { private func observeMenuBarPreference() {
guard let model else { return } guard let model else { return }
withObservationTracking { withObservationTracking {

View File

@@ -49,6 +49,26 @@ targets:
product: CalendarProvider product: CalendarProvider
- package: OnyxKit - package: OnyxKit
product: WeatherProvider product: WeatherProvider
postBuildScripts:
# Das Menüleistensymbol soll das Glyph aus dem App-Icon zeigen, nicht die
# abgerundete Kachel mit Verlauf. actool kompiliert das .icon-Bündel zu
# .icns und Assets.car und legt nichts Lesbares ins Bundle — und eine
# einzelne Datei daraus lässt sich nicht als Ressource referenzieren.
# Deshalb kopieren statt duplizieren: es bleibt bei einer Quelldatei.
- name: Glyph für die Menüleiste ins Bundle kopieren
script: |
SRC="$SRCROOT/Onyx/AppIcon.icon/Assets/quartz.svg"
DST="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/quartz.svg"
if [ -f "$SRC" ]; then
mkdir -p "$(dirname "$DST")"
cp "$SRC" "$DST"
else
echo "warning: $SRC fehlt — Menüleiste fällt auf ein SF-Symbol zurück"
fi
inputFiles:
- $(SRCROOT)/Onyx/AppIcon.icon/Assets/quartz.svg
outputFiles:
- $(BUILT_PRODUCTS_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/quartz.svg
settings: settings:
base: base:
PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.onyx PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.onyx