I know I’m very late to the party here, but until now I have avoided doing anything with Dark Mode.
The forum is riddled with Dark Mode related posts, but I see no summary anywhere of what one needs to do to implement Dark Mode support in a Xojo app. The Xojo Blog also has no guide. I don’t see anything in the docs, and the LR has almost nothing to say about it. [EDIT: actually yes it does - https://documentation.xojo.com/topics/user_interface/desktop/macos/supporting_macos_dark_mode.html ]
All I know so far is:
there is a switch in the IDE for supporting Dark mode
said switch is read only in code
there is an App event which fires when Dark Mode goes on or off
Colors have a IsDarkMode property
If I missed something please add to that list.
Question is: what as a designer do I actually need to do to support Dark Mode?
I guess:
turn on the switch
monitor the mode changed event
call different sets of graphics and drawing colors per state?
Judging from the number of related topics on the forum, there must be more to it than that?
It’s found in the Inspector (the right side) when you have Build Settings > Shared selected.
Mostly nothing unless you do custom drawing in your app.
No, this is not the best way to handle Dark Mode. The Paint event for any drawing you’re doing should be aware of IsDarkMode and select the colors necessary. This way, the operating system can decide when is the best time to draw.
if Color.IsDarkMode then
g.DrawingColor = LightGrey
else
g.DrawingColor = MidGrey
end if
There is an AppearanceChanged event in the App. I use this to send internal notifications to most windows and containers so that those can change whatever needs to be changed.
Thanks, that clears things up. I wondered why a color object should have this property. Now I see it’s a global lookup. (I had expected that to be an Application property).
I have a small library of subclassed objects I use in all my Xojo apps which do their own drawing. This is why I have avoided dealing with Dark Mode. For example, listboxes in my apps normally consist of 3 objects: a subclassed listbox which does custom drawing for background and text, a scrollbar object (the normal listbox scrollbars are not used) and a canvas for the header. So all of these will have to change their drawing in Dark Mode. Only the scrollbar should take care of itself.
Some of my apps use the NSStatusItem menubar icons. A customer complained that an icon drawn in black (trendy for non-Dark mode) doesn’t work in Dark Mode.
My IDE was locked up compiling, here’s sample code for MBS to set the StatusItem icon:
dim toIcon as new NSImageMBS(SpecialFolder.Resources.Child("myIcon.png"))
toIcon.isTemplate = true
toIcon.setSize(16, 16)
oStatusItemMBS.Image = toIcon
Don’t forget to check out Color Groups, which allow you to specify a dark and a light color (or a named color from the OS) which will automatically switch for you.
For instance, if you made a color group with a light gray and a medium gray and called it GrayColorGroup, you could just use the object in the Paint event like this:
g.DrawingColor = GrayColorGroup
and the framework will choose the right color for you without having to check IsDarkMode.
Thanks; I did not know about ColorGroup. In fact, I was about to embark on making my own class to manage Colors in a similar way, apparently reinventing the wheel …
I just double-checked, and ColorGroup is not mentioned anywhere on the UserGuide page, nor is it linked to on any of the DarkMode-related entries in the LR. Seems like it should at least be under “See Also” on all those pages.
EDIT: Looking into ColorGroup a bit more, seems like it should really get a paragraph on the UserGuide page.
For whatever it’s worth, trying to call a color from a ColorGroup in a project on a Mac running High Sierra (which of course doesn’t have a DarkMode) causes a hard crash:
The ColorGroup objects can be created, but not called. This makes them a no-go for me, because I regularly move code between machines running different OS versions.
This means ColorGroups can be used with the predefined system variables only for macOS 10.15 +. So in your case you have to use the constructor linked above.
But I am using those constructors. The ColorGroup objects are created in code and assigned colors with &c literals. The crash report clearly shows there is a call to an unrecognised selector (no Dark Mode in this OS).
Right. Since Xojo mediates between “easy-to-use functions” and the more complex OS level functions, the Xojo function should take care of avoiding calls on systems that do not support it, and gracefully handle it (as in this case: simply always choose the bright mode colors).
EditFieldGlobals.IsDarkMode() works on all systems and with older IDE versions.
EditFieldGlobals.AdjustColorForDarkMode() attempts to turn a generic color into one that’s suitable for dark mode. It’s not perfect but the best if you cannot predict the colors that are used (like in this project, which allows the user to choose arbitrary colors).
You can also check out GraffitiColors which predates ColorGroups but provides all system colors in whatever theme currently being used, custom theme change listener delegates, and system-wide Accessibility settings in the MacOS module, as well as the application of system effects if you’re building for macOS.