« Apple Maps in FileMak… | Home | Nine month till Europ… »

Alternating row colors for Dark Mode

We ported a bigger Xojo project to MacOS Mojave recently and in that process needed a good solution to update our alternating row colors for the list boxes. We used to have the usual code with "row mod 2 = 1" and picking a color for the every second row. But that is not perfect with a fixed color for switching between light and dark mode. And while the average user never switches the setting while using the application, the beta testers of course do that. So we needed a good solution for the project.

Xojo 2018r4 has the AppearanceChanged event for Application class, which is very handy to switch colors. Or you check with IsDarkMode function every time you draw something and decide the color. As CellBackgroundPaint event in the Listbox class is called for each cell, it can be called 100s of times just for redrawing the list once. So we like to optimize the handling and cache the colors.

In MBS Plugin we got last summer the dark mode additions for NSColorMBS class. The NSColorMBS.alternatingContentBackgroundColors function returns an array with two NSColor objects with the current preferred background colors for lists. So we want to use this, but not call it for every event as building an array and color objects costs a little bit performance for each draw.

So here is some example code:
Project "Listbox Row Colors.xojo_binary_project"
Class App Inherits Application
EventHandler Sub AppearanceChanged() QueryRowColors End EventHandler
EventHandler Sub Open() QueryRowColors End EventHandler
Shared Sub QueryRowColors() // our defaults RowColorEven = &cFFFFFF RowColorOdd = &cEEFFEE #if TargetMacOS then dim colors() as NSColorMBS = NSColorMBS.alternatingContentBackgroundColors if colors = nil then break return // should not happen end if if colors.Ubound = 1 then // 2 colors RowColorEven = colors(0).colorValue RowColorOdd = colors(1).colorValue end if #endif End Sub
Property Shared RowColorEven As color
Property Shared RowColorOdd As color
End Class
Class MainWindow Inherits Window
Control List Inherits Listbox
ControlInstance List Inherits Listbox
EventHandler Function CellBackgroundPaint(g As Graphics, row As Integer, column As Integer) As Boolean if me.ListIndex = row then // let Xojo draw selection else if row mod 2 = 0 then g.ForeColor = app.RowColorEven else g.ForeColor = app.RowColorOdd end if g.FillRect 0, 0, g.Width, g.Height end if End EventHandler
End Control
End Class
End Project
This example project will be included with future plugins. As you see this includes all parts necessary. We query the current colors when the application opens or the appearance changes. We cache the colors in RowColorEven and RowColorOdd shared properties. And in the CellBackgroundPaint event, we only draw unselected rows with the right color. For multi selection list boxes, you can check selected() function instead. We use #if to only reference plugin for MacOS and not for Windows. And for Windows we also need the default colors there.

By the way, the plugin hides an extra complexity. The alternatingContentBackgroundColors function is only available for macOS 10.14 or newer and we switch to an older function for older MacOS versions and you can call it everywhere without problems.
23 01 19 - 20:53