Sign in Join
Wiki copytemporary

Dark theme: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Dark theme

OldNewLine
1+How to detect whether the operating system is using a dark theme?
2+
3+## Universal method
4+
5+The following method is based on the GUI colors. It works on all platforms but those colors are not updated when the theme is changed while the application is running. Thus it cannot allow to detect if the dark mode is toggled.
6+
7+### Example 1
8+
9+``` pascal
10+// by "Alextp" from Lazarus forum
11+function IsDarkTheme: boolean;
12+const
13+ cMax = $A0;
14+var
15+ N: TColor;
16+begin
17+ N:= ColorToRGB(clWindow);
18+ Result:= (Red(N)<cMax) and (Green(N)<cMax) and (Blue(N)<cMax);
19+end;
20+```
21+
22+### Example 2
23+
24+``` pascal
25+// by "Hansaplast" & "Alextp" from Lazarus forum
26+function IsDarkTheme: boolean;
27+ function _Level(C: TColor): double;
28+ begin
29+ Result:= Red(C)*0.3 + Green(C)*0.59 + Blue(C)*0.11;
30+ end;
31+begin
32+ Result:= _Level(ColorToRGB(clWindow)) < _Level(ColorToRGB(clWindowText));
33+end;
34+```
35+
36+## Windows method
37+
38+### Example 1
39+
40+``` pascal
41+// by "jwdietrich" from Lazarus forum
42+uses
43+ Windows, Win32Proc, Registry;
44+
45+// IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false)
46+function IsDarkTheme: boolean;
47+const
48+ KEYPATH = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
49+ KEYNAME = 'AppsUseLightTheme';
50+var
51+ LightKey: boolean;
52+ Registry: TRegistry;
53+begin
54+ Result := false;
55+ Registry := TRegistry.Create;
56+ try
57+ Registry.RootKey := HKEY_CURRENT_USER;
58+ if Registry.OpenKeyReadOnly(KEYPATH) then
59+ begin
60+ if Registry.ValueExists(KEYNAME) then
61+ LightKey := Registry.ReadBool(KEYNAME)
62+ else
63+ LightKey := true;
64+ end
65+ else
66+ LightKey := true;
67+ Result := not LightKey
68+ finally
69+ Registry.Free;
70+ end;
71+end;
72+```
73+
74+## macOS method
75+
76+### Example 1
77+
78+``` pascal
79+// by "trev" from Lazarus forum
80+
81+{ returns true, if this app runs on macOS 10.14.0 Mojave or newer }
82+function MojaveOrNewer: boolean;
83+var
84+ minOsVer: NSOperatingSystemVersion;
85+begin
86+ //Setup minimum version (Mojave)
87+ minOsVer.majorVersion:= 10;
88+ minOsVer.minorVersion:= 14;
89+ minOsVer.patchVersion:= 0;
90+
91+ // Check minimum version
92+ if(NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer)) then
93+ Result := True
94+ else
95+ Result := False;
96+end;
97+
98+{ The following two functions were suggested by Hansaplast at https://forum.lazarus.freepascal.org/index.php/topic,43111.msg304366.html }
99+
100+// Retrieve key's string value from user preferences. Result is encoded using NSStrToStr's default encoding.
101+function GetPrefString(const KeyName : string) : string;
102+begin
103+ Result := NSStringToString(NSUserDefaults.standardUserDefaults.stringForKey(NSStr(@KeyName[1])));
104+end;
105+
106+// IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false)
107+function IsDarkTheme: boolean;
108+begin
109+ Result := false;
110+ if MojaveOrNewer then
111+ Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0;
112+end;
113+```
114+
115+#### Demo
116+
117+``` pascal
118+unit Unit1;
119+
120+{$mode objfpc}{$H+}
121+{$modeswitch objectivec1}
122+
123+interface
124+
125+uses
126+ Classes, Forms, Dialogs, StdCtrls, SysUtils,
127+ CocoaAll, CocoaUtils, MacOSAll;
128+
129+type
130+
131+ { TForm1 }
132+
133+ TForm1 = class(TForm)
134+ Button1: TButton;
135+ procedure Button1Click(Sender: TObject);
136+ private
137+
138+ public
139+
140+ end;
141+
142+var
143+ Form1: TForm1;
144+
145+implementation
146+
147+{$R *.lfm}
148+
149+{ TForm1 }
150+
151+{ returns true, if this app runs on macOS 10.14 Mojave or newer }
152+function MojaveOrNewer: boolean;
153+var
154+ minOsVer: NSOperatingSystemVersion;
155+begin
156+ // Setup minimum version (Mojave)
157+ minOsVer.majorVersion:= 10;
158+ minOsVer.minorVersion:= 14;
159+ minOsVer.patchVersion:= 0;
160+
161+ // Check minimum version
162+ if(NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer)) then
163+ Result := True
164+ else
165+ Result := False;
166+end;
167+
168+function GetPrefString(const KeyName : string) : string;
169+begin
170+ Result := NSStringToString(NSUserDefaults.standardUserDefaults.stringForKey(NSStr(@KeyName[1])));
171+end;
172+
173+function IsDarkTheme: boolean;
174+begin
175+ Result := false;
176+ if MojaveOrNewer then
177+ Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0;
178+end;
179+
180+procedure TForm1.Button1Click(Sender: TObject);
181+begin
182+ if(IsDarkTheme) then
183+ ShowMessage('Dark mode')
184+ else
185+ ShowMessage('Light mode');
186+end;
187+
188+end.
189+```
190+
191+### Example 2
192+
193+Catalina added an "Auto" option where the computer switches between Light and Dark modes depending on time of day. The 'AppleInterfaceStyle' method apparently doesn't work if auto is enabled.
194+
195+The solution is to get the NSApp.effectiveAppearance string which will be one of a number of values including 'NSAppearanceNameAqua' (standard light mode) and 'NSAppearanceNameDarkAqua' (standard dark mode). Google these to see the whole list which includes other light and dark modes with added contrast. The effectiveAppearance is correct in all modes including when auto mode kicks in.
196+
197+Tested on Mojave, Catalina, Big Sur.
198+
199+``` pascal
200+// by "Clover" from Lazarus forum
201+function IsMacDarkMode: Boolean;
202+var
203+ sMode: string;
204+begin
205+ //sMode := CFStringToStr( CFStringRef( NSUserDefaults.StandardUserDefaults.stringForKey( NSSTR('AppleInterfaceStyle') ))); // Doesn't work in auto mode
206+ sMode := CFStringToStr( CFStringRef( NSApp.effectiveAppearance.name ));
207+ Result := Pos('Dark', sMode) > 0;
208+end;
209+```
210+
211+#### Demo
212+
213+``` pascal
214+unit Unit1;
215+
216+{$mode objfpc}{$H+}
217+{$modeswitch objectivec1}
218+
219+interface
220+
221+uses
222+ Classes, Forms, Dialogs, StdCtrls,
223+ CocoaAll, CocoaUtils, MacOSAll;
224+
225+type
226+
227+ { TForm1 }
228+
229+ TForm1 = class(TForm)
230+ Button1: TButton;
231+ procedure Button1Click(Sender: TObject);
232+ private
233+
234+ public
235+
236+ end;
237+
238+var
239+ Form1: TForm1;
240+
241+implementation
242+
243+{$R *.lfm}
244+
245+{ TForm1 }
246+
247+function IsMacDarkMode: Boolean;
248+var
249+ sMode: string;
250+begin
251+ sMode := CFStringToStr( CFStringRef( NSApp.effectiveAppearance.name ));
252+ Result := Pos('Dark', sMode) > 0;
253+end;
254+
255+procedure TForm1.Button1Click(Sender: TObject);
256+begin
257+ if(IsMacDarkMode) then
258+ ShowMessage('Dark mode')
259+ else
260+ ShowMessage('Light mode');
261+end;
262+
263+end.
264+```
265+
266+### Opting out of dark mode
267+
268+Mac systems automatically opt in any application linked against the macOS 10.14 or later SDK to both light and dark appearances. You can opt out of dark mode by including the NSRequiresAquaSystemAppearance key (with a value of YES) in your application’s [[macOS property list files|Info.plist]] property list file. Setting this key to YES causes the system to ignore the user's preference and always apply a light appearance to your application.
269+
270+### Opting in to dark mode
271+
272+If you build your application against an earlier SDK but still want to support Dark Mode, include the NSRequiresAquaSystemAppearance key (with a value of NO) in your application's [[macOS property list files|Info.plist]] property list file. Do so only if your application's appearance looks correct when running in macOS 10.14 and later with Dark Mode enabled.
273+
274+## See Also
275+
276+- [[macOS extensions]]
277+
278+### External links
279+
280+- [Apple: Mojave Dark Mode](https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/).
281+
282+---
283+*From the Free Pascal wiki, page "Dark theme", as it stood on 2021-01-31 after a change by Jwdietrich, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
284+Category: Code Snippets
285+Category: FPC
286+Category: Platform-sensitive development
287+Category: Windows
288+Category: macOS

Back to history