eclipse.platform.swt
eclipse.platform.swt copied to clipboard
[macOS] Decorations#setImage() overwrites Dock icon specified in App Bundle
Describe the bug
Currently, there are three possible sources for the Dock icon on macOS in the order of precedence:
-
An explicitly set icon via -Xdock:icon=/path/to/icon.icns which calls NSApp setApplicationIconImage
-
An implicitly set icon via org.eclipse.swt.widgets.Decorations.setImage(Image) org.eclipse.swt.widgets.Decorations.setImages(Image[]) which in passed down to the Dock also via NSApp setApplicationIconImage
-
An implicitly set icon in a Bundled.app distrubution via CFBundleIconName / CFBundleIconFile in the Info.plist file
1 and 2 use legacy API NSApp setApplicationIconImage which only supports a single fixed NSImage, i.e. there is no support for dark/light mode and or modern look & feel such as Liquid Glass.
Only 3 (app bundle) supports dynamic icons.
The problem is that 2) currently overwrites whatever 3) has set. On top of that, the Decorations are typically cross-platform, so in fact this will prevents using a macOS-specific icon at all.
We want to prefer 3) over 2), i.e. only pass down the decorations whenever we are not an app bundle with a declared image.
Similar coding also exists in the JVM, where the dock icon is only set if we are not in an app bundle that specifies an icon:
https://github.com/openjdk/jdk21u/blob/8c322f5953ae161d793213f92d13a1f53d995883/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m#L280-L290
To Reproduce
Build an RCP product as a macOS Product.App, which defines a macOS-specific icon in the .product configuration and also launches a branded RCP product.
When the product launches, it shortly shows the icon defined in the Product.app/Info.plist file.
As soon as SWT initializes it overwrites the icon with whatever platform-agnostic icon is set in the product branding.
Expected behavior
The icon specified in the Product.App should be shown in the Dock.
Screenshots
During launcher:
After SWT initialization:
Environment:
- Select the platform(s) on which the behavior is seen:
-
- [ ] All OS
-
- [ ] Windows
-
- [ ] Linux
-
- [X] macOS
Workaround (or) Additional context
Specifying -Xdock:icon=/path/to/static/image.icns prevents SWT from changing the Dock image, but this is legacy API and does not support modern / dynamic icon features
Our RCP app (and Eclipse itself) seems to need option 1 (-Xdock:icon=/path/to/icon.icns) more than it does 3.
We have both, and removing option 1 leads to the wrong icon but removing option 3 doesn't seem to make any difference.
Our RCP app (and Eclipse itself) seems to need option 1 (
-Xdock:icon=/path/to/icon.icns) more than it does 3.We have both, and removing option 1 leads to the wrong icon but removing option 3 doesn't seem to make any difference.
The proposed change does not touch 1). If you set -Xdock:icon=/path/to/icon.icns, it will still have priority.
The proposal is to prefer 3) over 2), i.e. prefer a bundled (Assets.car) icon over icon set via SWT Decorations.
Assets.car is the only solution that supports dynamic icons (liquid glass, dark/light, ...)
How does an Assets.car file work? Is this created in Xcode and declared in the Info.plist file?
How does an
Assets.carfile work? Is this created in Xcode and declared in theInfo.plistfile?
See for example here: https://www.hendrik-erz.de/post/supporting-liquid-glass-icons-in-apps-without-xcode
Basically you create an ".icon" file with "Icon Composer" on macOS. Then use use XCode or "actool" command line tool to create a the .car file that is then referenced in the info.plist file.
When materializing products with P2 (PDE/Tycho), the .product configuration:
<launcher name="Foo">
<macosx icon="icons/AppIcon.icns"/>
</launcher>
currently creates the following Info.plist:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plist version="1.0">
<dict>
<!-- ... -->
<key>CFBundleIconFile</key>
<string>AppIcon.icns</string>
<!-- ... -->
</dict>
</plist>
This is legacy as well. To fully make use of Assets.car, you'd need an CfBundleIconName entry:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plist version="1.0">
<dict>
<!-- ... -->
<key>CFBundleIconName</key> <!-- priority over CFBundleIconFile, if supported by OS -->
<string>AppIcon</string> <!-- name of a resource inside Resources/Assets.car -->
<key>CFBundleIconFile</key>
<string>AppIcon.icns</string>
<!-- ... -->
</dict>
</plist>
This currently cannot be auto-generated by P2's publisher, but you can fiddle around with that, e.g. with maven-antrun-plugin which adds the Resources/Assets.car file to the binary artifact and updates the Info.plist accordingly.
How does an
Assets.carfile work? Is this created in Xcode and declared in theInfo.plistfile?See for example here: https://www.hendrik-erz.de/post/supporting-liquid-glass-icons-in-apps-without-xcode
Basically you create an ".icon" file with "Icon Composer" on macOS. Then use use XCode or "actool" command line tool to create a the .car file that is then referenced in the info.plist file.
Thanks.
Note that the old icon file needs to be kept:
Don’t just throw out your existing .icns-file. There are still many out there with Macs that don’t run the newest macOS, and if you remove the .icns-file, the app will break for all of these people. For the coming years, be prepared to ship both the .icns-file and the new Liquid Glass version.
This currently cannot be auto-generated by P2's publisher,
Can the publisher be enhanced in a way that for ".car" files a CFBundleIconName entry is created?
When materializing products with P2 (PDE/Tycho), the
.productconfiguration:
How can we handle compatibility with older macOS versions. That means how can we provide details in the product file so that both CFBundleIconName and CFBundleIconFile are created in the info.plist ?
Can the publisher be enhanced in a way that for ".car" files a CFBundleIconName entry is created?
The icon handling is done in
https://github.com/eclipse-equinox/p2/blob/cc3868542ee18f101b56a5a02916b148ad13819f/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/internal/p2/publisher/eclipse/BrandingIron.java
This would have to be enhanced accordingly and the model of the .product configuration as well - similar to what we did when we added support for CfBundleUrlType entries for the link handlers.
How can we handle compatibility with older macOS versions. That means how can we provide details in the product file so that both CFBundleIconName and CFBundleIconFile are created in the info.plist ?
I think we should allow to configure both:
- The legacy icon via the existing syntax in
<launcher name="Foo"> <macosx icon="icons/AppIcon.icns"/> </launcher> - Maybe add a more general syntax here, something like
<launcher name="Foo"> <macosx> <icon>iconsAppIcon.icns</icon> <additionalResources>resources</additionalResources> <!-- local folder whichs is copied to the Bundle under Resources during publishing --> <additionalPlistConfigurations> <!-- ... --> </additionalPlistConfigurations> </macosx> </launcher>
- Maybe add a more general syntax here, something like
That sounds reasonable to me...