Darkmode image color black -> white

On my app I have some rounded buttons fill with images. It’s small image like those :
Capture d’écran 2023-05-16 à 08.21.08

How can I change the color from black to white of this image when app run on dark mode ?

I generally use an image without transparency (black icon with white background) and then simply use it as a mask

For example:

Function DrawMaskedImage(myPictureImage As Picture, myPictureColor As Color) As Picture

Var p As New Picture(myPictureImage.Width, myPictureImage.Height)

p.Graphics.DrawingColor = myPictureColor
p.Graphics.fillRect(0, 0, p.Width, p.Height)
p.ApplyMask(myPictureImage)
Return p

End Function

myPictureImage is your picture in black with white background
myPictureColor is the color that will appear instead of the black one
The white background will become transparent

1 Like

Thank @Giulio_D_Anna this looks great. And how do you manage dark mode enabled by user?

Unfortunately, I do not have a Mac but use Windows and, for windows, it is not user-managed (I am waiting for the xaml islands for this).
However, if for mac the handling is similar to when changing the mode from system, I use canvas for the custom buttons so that the refresh happens automatically.
An alternative solution could be to use a timer that gives the OK to redraw when there is a theme change

A doubt occurred to me, perhaps I misinterpreted the question,
do you mean this?

Var p As Picture
Var c As Color

If Color.IsDarkMode Then c = Color.RGB(255, 255, 255) Else c = Color.RGB(0, 0, 0)
p = DrawMaskedImage(myPictureImage, c)

I use PDFs wherever possible. Those can be easily inverted.

For toolbar buttons isTemplate can be used which inverts the images automatically:

dim theImage as new NSImageMBS(proxy_outline)
theImage.isTemplate = true

I use a custom function (Windows/Mac) to get the image depending on Color.IsDarkMode().

My images are named accordingly. Also manage the AppearanceChanged if the user is switching Dark to Light while windows are open.

Because sometimes it’s not just all white/black but some parts are colored with some white or black so this was the solution to it.

1 Like

Yes, of course, that’s what I did initially too. It depends on what you want to do.
I came to the mask choice simply because then you can also give the user the option of dynamically choosing the colour of the style (both dark and lite) simply with a single jpg or 2 if an icon consists of (“white or other”/“black or other” + primary colour)

Probably closed now, but I filed a feature request some time ago to get Xojo to properly support template images.

Not interested.

1 Like

Here is a currently active Template image request, which is certainly worthy of upvotes, currently I’m the only one up voting it.

https://tracker.xojo.com/xojoinc/xojo/-/issues/53302

2 Likes

You need something with a Paint event so you can draw correctly. I don’t see a border or anything around your icon, would you be able to switch the button for a Canvas? I normally recommend against Canvas as button, but when Xojo doesn’t provide, we simply have no choice.

To handle the correct colors automatically, you can set the masked fill-color to Color.TextColor. If you can turn your button into a Canvas, this color will update automatically when the user switches modes.

1 Like

On the Mac you can never draw in the right colour for a toolbar. Due to things like desktop tinting etc. Even the API colours do not return anything that works correctly. The only way of doing the right is to use Template images. None of the greys are correct and forget trying to do the highlight colour on a segment button.

You’re seeing a combination of problems here.

  1. Apple do not list all the colors they use and some just don’t match anything.
  2. Xojo still doesn’t support Apple’s dynamic colors, which means they MUST be rasterized and Xojo only supports the Generic color space, so you can get 'em close, real close, but they’re not the same. The irony is that I bet underneath Xojo then uses the color values to create a CGColor which actually supports color spaces.
  3. The dynamic colors work best when applied to views, not drawn. i.e. The ImageWell has a contentTintColor property. You specify a template image and set the color. Xojo doesn’t support either of these properties, nor the properties to adjust scaling and the frame of an ImageWell.
  4. Because Xojo doesn’t support dynamic colors, you can not even use 'em correctly with labels as Xojo only accepts the rasterized color.
  5. If you do decide that the only way is to draw with Appe’s dynamic colors, it is best to use NS drawing primitives as they will get a closer match than you can with Xojo’s framework.

I put in all the work to help Xojo developers access this functionality and learn from my experience, but Xojo demonstrated that they don’t care about any of it.

At the end of the day, Xojo developers are going to continue to be at a disadvantage in creating modern Mac apps, that is until Xojo hires someone with the experience and knowledge to drag their framework into this current decade.

3 Likes

I use a SFSymbols module I created, which is a modified version of the code posted by Javier Menendez. It uses the Template flag and ensures that the toolbar looks perfect no matter what settings are used in macOS. It also supports adding SFSymbols to buttons and other controls that support images. It also means that Dark / Light mode is automatically supported.

1 Like

I would recommend that you double check the code you’re using, there was some pretty inefficient code floating around this forum.

It was drawing the SF Symbol into a Xojo picture, then creating a NSImage from the Xojo picture to set onto controls. Which not only rasterizes the vector image to bitmap, losing the template property, but is wasting CPU cycles, energy and memory.

Ideally (and in the OAK source code), you should use a declare to get the NSImage and then a second declare to set it onto the control. It preserves all the goodness of SF Symbols, while not wasting CPU, energy and memory. All the declares you should need are part of OAK.

should as I absolutely may have missed some.

Another way to do it, especially if you wanna support older versions of the macOS, is to use App Wrapper to rasterize the SF Symbols (it can generate multi-resolution TIFF images, including multiple sizes and their Retina equivalents), then load them in your app via declares, set the template property and use 'em in your interface. If you load 'em via declares the OS will automatically cache 'em for you, so subsequent calls avoid reloading the data.

At some point there was a feedback asking Xojo to support NSImages internally, as that would be really helpful. NSImages should never be rasterized except by the actual API that draws 'em to a bitmap context.

Oh… I forgot one thing about Apple’s Dynamic colors. while you can set them as the background to nearly every single view, some colors will only work correctly when set as the background to specific views. Which is not only bizarre to me, but IMHO also quite wasteful, as it means you may need an additional view, just to display the color correctly.

A bit like containerControls don’t respond to mouse enter or mouse exit, so you need to waste memory by adding a canvas to a containerControls to get these events.

1 Like