Friday, August 14, 2020

Implementing SetIcon()

After researching what it takes to set a window icon dynamically, I moved on to implementing it. Each platform handled setting the icon differently, with some platforms having more capabilities than others. These differences presented a challenge as to how to create standard behavior between the platforms. It also generated the question of how to enable developers to use the SetIcon() function to maximize the use of each platform's capabilities.

Where icons can be changed
Three icon display areas can be set dynamically at runtime: the window, taskbar, and alt-tab display. The Mac doesn't have a window icon, so only the dock and cmd-tab display can be changed. Depending on the distribution, Linux may or may not have an icon on the window, but regardless the two other display icons can be changed.

Platform differences
Each platform-specific method for changing the icon will change all icon display areas with the given icon. Where the platforms differ is how different icon display areas can be set independently. On Windows, two different messages can be sent to change either the window icon or both the taskbar and alt-tab icons. On Linux, the message to set the icon can contain multiple icons. The window manager will pick from the list of icons the best fitting icon for each display area. On Mac, there is no way to set icons independently at runtime.

More functionality
I felt that developers should be able to set multiple sized icons if they wanted to. I made the behavior of the SetIcon() function match across all platforms when it is first called. When the function is subsequently called, the Mac implementation continues to behave the same, while Windows and Linux behavior differs. The implementation of these two platforms attempts to set individual icons based on the size of the icon provided to them.

Mac functionality limited
I attempted to give the Mac implementation the same behavior as the other platforms for consecutive calls. However, I was unable to find a way to do so. To set an icon on the Mac dynamically, you create an NSImage and assign it as the applications icon image. I use the icon pixel array to make the bitmap representation that gets added to the NSImage I create. One of the interesting features of NSImage is the ability to add multiple image representations. There is even a handy function that will grab the best fit of these representations for a given area. I thought adding different sized icons as image representations would have a similar result as the icon list on Linux. However, this was not the case. I also tried naming each icon image and appending the names with @1, @2, @3, etc. This how icons of different sizes are bundled together in Xcode. Dynamically naming the image representations doesn't work, and I have found documentation that suggests icon bundling can only be set up through Xcode and not dynamically at runtime.

Feature notes
There are few things anyone using the SetIcon() function should be aware of. Windows 10 icons are 16x16 and 24x24. Linux can vary depending on the distribution. On my version of Linux Mint, icons are 16x16 and 48x48. If you don't use these sizes, both platforms will scale the icon to fit the displayed area. Scaled icons look blurry, which is why I am providing the dimensions I have found. Setting icons on the Mac is not as forgiving, and the icons do not display correctly when outside of a designated size. Acceptable sizes for the Mac are 16x16, 32x32, 48x48, 128x128, 256x256, 512x512, and 1024x1024. I have tested all of these icon sizes, except for the last two.

Feature complete
A convenient function to dynamically set icons is a fun feature. It is less complicated than figuring out how to bundle the app icon on each platform. The subsequent call feature of the SetIcon() function provides a way to keep those icons crisp and professional looking. Plus, the dynamic nature of the SetIcon() function enables developers to be creative about it. I imagine an icon of the doom guy changing as the player moves around or takes damage. Overall, this is another fun feature I am glad I got to work on.

No comments:

Post a Comment