Dark mode pictures management


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 ?

Thank you

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.

Here’s a Feedback case you might like to sign up to. <https://xojo.com/issue/52913>

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.

Thank you for the answers, I will try to create a mask but never did it.

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 )

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.

We are aware of this issue. There’s no need for another report.

So I see. Thanks. <https://xojo.com/issue/34926>

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.