I wanted to know what is the best way to manage icons (picture) for macOS Dark/Light mode or how do you manage them?
I mainly use icons in toolbars / segmented controls / picture buttons.
Should I set two versions of the image (one for the dark and one for the light in @1x and @2x) and check isDarkMode() or is it possible to set only one version of the image and apply a color (i.e.: white) over the non transparent content and set back the created/transformed image back to the control ?
I use two sets of images and a ton of boilerplate code. I’m hoping that Xojo will give us ImageGroups in the future, in the same manner as the new and useful ColorGroups, so that the framework will handle this for us.
right now there is no automatic mechanism as there is for @1x vs @2x
and yes there is a way to make a single version with color… just make the MASK the icon image
my current app has over 600 images for GUI based icons
I use grayscale images and declares to make them “Template” images. This way when you use declares to set them as an icon of a control, the macOS will render them in the appropriate color automatically for you.
I think I have a feedback request somewhere to enable this functionality in Xojo.
@Sam_Rowlands
I know I’m waking a dead thread here, but care to share the Template Declares to set them as icons of controls? I’m attempting to create a toolbar using template images.
Hey Ian,
Once you start down this path, there is no turning back…
The following code will create a Xojo picture of a circle, set it to the first item in the toolbar, which will auto adjust to the OS theme.
Dim p as picture = me.bitmapForCaching( 32, 32 )
Dim g as graphics = p.graphics
g.pensize = 2
g.drawOval 2, 2, g.width - 4, g.height - 4
#if targetMacOS then
// --- Code written by Sam Rowlands, Feb 26th 2021
// Declares extracted from the Ohanaware App Kit. https://ohanaware.com/appkit/
declare Function NSArrayObjectAtIndex lib "Foundation" selector "objectAtIndex:" ( NSArrayInstance as integer, index as UInteger ) as integer
declare Sub NSImageSetTemplate lib "AppKit" selector "setTemplate:" ( NSImageInstance as ptr, assigns value as boolean )
declare Function NSWindowToolbar lib "AppKit" selector "toolbar" ( NSWindowInstance as integer ) as integer
declare Function NSToolbarItems lib "AppKit" selector "items" ( NSToolbarInstance as integer ) as integer
declare sub NSToolbarItemSetImage lib "AppKit" selector "setImage:" ( NSToolbarItemInstance as integer, value as ptr )
// --- Get a NSImage from the Xojo picture.
Dim nsi as ptr = p.copyOSHandle( picture.handleType.MacNSImage )
// --- Mark it as a template image
NSImageSetTemplate( nsi ) = true
// --- Get the toolbar from the window, then a list of the items. If you move this code inside of a control,
// make sure you update it from "me.handle" to "me.truewindow.handle" so it gets the window.
Dim toolbarItemArray as integer = NSToolbarItems( NSWindowToolbar( me.handle ) )
// --- We extract the first item from the list and set our NSImage as it's image.
// To set the image on a different item, change "0" to the index of the item.
NSToolbarItemSetImage( NSArrayObjectAtIndex( toolbarItemArray, 0 ), nsi )
#endIf
Hi, Sam thanks for that. I had worked out most of that in the last day or so, since asking. I hadn’t got the bitmapForCaching part.
I did the rest but found that if you set the toolbar item .Enabled = True / False the image disappeared. I figured that since the control hasn’t had a bitmap set it was clearing the image. The bitmapForCaching may solve the problem. I’ll take a look.
I was also wondering if there was a method of accessing the control by name rather than index (which is a weak point as it stands. If you add, remove or reorder the toolbar you have to refactor the indices used to access them). I tried looking at the objectWithName: but I couldn’t get it to work. It would also assume that Xojo informs the underlying API of the names of the elements of the items, which may well not be the case.
AFAIK you’d have to step through the object array and compare the objects. You can do this by either matching it the toolbaritem.handle or by reading the properties of the object (NSToolbarItem).
objectWithName: is part of the scripting bridge, which I have zero experience with.