Difference between revisions of "Dark theme"
(new page) |
m (→Opting in to dark mode: Fix two of my typos) |
||
(26 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | How to detect dark theme | + | How to detect whether the operating system is using a dark theme? |
− | =Universal method= | + | ==Universal method== |
+ | ===Example 1=== | ||
<syntaxhighlight lang="pascal"> | <syntaxhighlight lang="pascal"> | ||
− | // by " | + | // by "Alextp" from Lazarus forum |
+ | function IsDarkTheme: boolean; | ||
+ | const | ||
+ | cMax = $A0; | ||
var | var | ||
− | + | N: TColor; | |
begin | begin | ||
− | + | N:= ColorToRGB(clWindow); | |
− | + | Result:= (Red(N)<cMax) and (Green(N)<cMax) and (Blue(N)<cMax); | |
− | + | end; | |
</syntaxhighlight> | </syntaxhighlight> | ||
− | = | + | ===Example 2=== |
+ | <syntaxhighlight lang="pascal"> | ||
+ | // by "Hansaplast" & "Alextp" from Lazarus forum | ||
+ | function IsDarkTheme: boolean; | ||
+ | function _Level(C: TColor): double; | ||
+ | begin | ||
+ | Result:= Red(C)*0.3 + Green(C)*0.59 + Blue(C)*0.11; | ||
+ | end; | ||
+ | begin | ||
+ | Result:= _Level(ColorToRGB(clWindow)) < _Level(ColorToRGB(clWindowText)); | ||
+ | end; | ||
+ | </syntaxhighlight> | ||
− | ==Example 1== | + | == Windows method == |
+ | |||
+ | === Example 1 === | ||
<syntaxhighlight lang="pascal"> | <syntaxhighlight lang="pascal"> | ||
Line 23: | Line 40: | ||
// IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false) | // IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false) | ||
− | function | + | function IsDarkTheme: boolean; |
const | const | ||
KEYPATH = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'; | KEYPATH = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'; | ||
Line 44: | Line 61: | ||
else | else | ||
LightKey := true; | LightKey := true; | ||
− | + | Result := not LightKey | |
finally | finally | ||
Registry.Free; | Registry.Free; | ||
Line 50: | Line 67: | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
− | ==Example 1== | + | == macOS method == |
+ | |||
+ | === Example 1 === | ||
<syntaxhighlight lang="pascal"> | <syntaxhighlight lang="pascal"> | ||
− | // by " | + | // by "trev" from Lazarus forum |
− | + | ||
− | + | { returns true, if this app runs on macOS 10.14.0 Mojave or newer } | |
− | + | function MojaveOrNewer: boolean; | |
− | |||
− | |||
var | var | ||
− | + | minOsVer: NSOperatingSystemVersion; | |
− | |||
begin | begin | ||
− | + | //Setup minimum version (Mojave) | |
− | + | minOsVer.majorVersion:= 10; | |
− | + | minOsVer.minorVersion:= 14; | |
− | + | minOsVer.patchVersion:= 0; | |
− | + | ||
− | + | // Check minimum version | |
− | + | if(NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer)) then | |
− | + | Result := True | |
− | + | else | |
− | + | Result := False; | |
− | |||
− | |||
− | |||
end; | end; | ||
Line 90: | Line 101: | ||
// IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false) | // IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false) | ||
− | function | + | function IsDarkTheme: boolean; |
+ | begin | ||
+ | Result := false; | ||
+ | if MojaveOrNewer then | ||
+ | Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0; | ||
+ | end; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== Demo ==== | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | unit Unit1; | ||
+ | |||
+ | {$mode objfpc}{$H+} | ||
+ | {$modeswitch objectivec1} | ||
+ | |||
+ | interface | ||
+ | |||
+ | uses | ||
+ | Classes, Forms, Dialogs, StdCtrls, SysUtils, | ||
+ | CocoaAll, CocoaUtils, MacOSAll; | ||
+ | |||
+ | type | ||
+ | |||
+ | { TForm1 } | ||
+ | |||
+ | TForm1 = class(TForm) | ||
+ | Button1: TButton; | ||
+ | procedure Button1Click(Sender: TObject); | ||
+ | private | ||
+ | |||
+ | public | ||
+ | |||
+ | end; | ||
+ | |||
+ | var | ||
+ | Form1: TForm1; | ||
+ | |||
+ | implementation | ||
+ | |||
+ | {$R *.lfm} | ||
+ | |||
+ | { TForm1 } | ||
+ | |||
+ | { returns true, if this app runs on macOS 10.14 Mojave or newer } | ||
+ | function MojaveOrNewer: boolean; | ||
+ | var | ||
+ | minOsVer: NSOperatingSystemVersion; | ||
+ | begin | ||
+ | // Setup minimum version (Mojave) | ||
+ | minOsVer.majorVersion:= 10; | ||
+ | minOsVer.minorVersion:= 14; | ||
+ | minOsVer.patchVersion:= 0; | ||
+ | |||
+ | // Check minimum version | ||
+ | if(NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer)) then | ||
+ | Result := True | ||
+ | else | ||
+ | Result := False; | ||
+ | end; | ||
+ | |||
+ | function GetPrefString(const KeyName : string) : string; | ||
+ | begin | ||
+ | Result := NSStringToString(NSUserDefaults.standardUserDefaults.stringForKey(NSStr(@KeyName[1]))); | ||
+ | end; | ||
+ | |||
+ | function IsDarkTheme: boolean; | ||
begin | begin | ||
Result := false; | Result := false; | ||
Line 96: | Line 173: | ||
Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0; | Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0; | ||
end; | end; | ||
+ | |||
+ | procedure TForm1.Button1Click(Sender: TObject); | ||
+ | begin | ||
+ | if(IsDarkTheme) then | ||
+ | ShowMessage('Dark mode') | ||
+ | else | ||
+ | ShowMessage('Light mode'); | ||
+ | end; | ||
+ | |||
+ | end. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | ==Example 2== | + | === Example 2 === |
+ | |||
Catalina added an "Auto" option where the computer switches between Light and Dark modes depending on time of day. | 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. | The 'AppleInterfaceStyle' method apparently doesn't work if auto is enabled. | ||
Line 117: | Line 205: | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | ==== Demo ==== | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | unit Unit1; | ||
+ | |||
+ | {$mode objfpc}{$H+} | ||
+ | {$modeswitch objectivec1} | ||
+ | |||
+ | interface | ||
+ | |||
+ | uses | ||
+ | Classes, Forms, Dialogs, StdCtrls, | ||
+ | CocoaAll, CocoaUtils, MacOSAll; | ||
+ | |||
+ | type | ||
+ | |||
+ | { TForm1 } | ||
+ | |||
+ | TForm1 = class(TForm) | ||
+ | Button1: TButton; | ||
+ | procedure Button1Click(Sender: TObject); | ||
+ | private | ||
+ | |||
+ | public | ||
+ | |||
+ | end; | ||
+ | |||
+ | var | ||
+ | Form1: TForm1; | ||
+ | |||
+ | implementation | ||
+ | |||
+ | {$R *.lfm} | ||
+ | |||
+ | { TForm1 } | ||
+ | |||
+ | function IsMacDarkMode: Boolean; | ||
+ | var | ||
+ | sMode: string; | ||
+ | begin | ||
+ | sMode := CFStringToStr( CFStringRef( NSApp.effectiveAppearance.name )); | ||
+ | Result := Pos('Dark', sMode) > 0; | ||
+ | end; | ||
+ | |||
+ | procedure TForm1.Button1Click(Sender: TObject); | ||
+ | begin | ||
+ | if(IsMacDarkMode) then | ||
+ | ShowMessage('Dark mode') | ||
+ | else | ||
+ | ShowMessage('Light mode'); | ||
+ | end; | ||
+ | |||
+ | end. | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === Opting out of dark mode === | ||
+ | |||
+ | 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|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. | ||
+ | |||
+ | === Opting in to dark mode === | ||
+ | |||
+ | 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|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. | ||
+ | |||
+ | === See also === | ||
+ | |||
+ | * [[macOS extensions]] | ||
+ | |||
+ | === External links === | ||
+ | |||
+ | * [https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/ Apple: Mojave Dark Mode]. | ||
+ | |||
+ | [[Category:FPC]] | ||
+ | [[Category:macOS]] | ||
+ | [[Category:Windows]] | ||
+ | [[Category:Code Snippets]] |
Revision as of 07:42, 30 August 2020
How to detect whether the operating system is using a dark theme?
Universal method
Example 1
// by "Alextp" from Lazarus forum
function IsDarkTheme: boolean;
const
cMax = $A0;
var
N: TColor;
begin
N:= ColorToRGB(clWindow);
Result:= (Red(N)<cMax) and (Green(N)<cMax) and (Blue(N)<cMax);
end;
Example 2
// by "Hansaplast" & "Alextp" from Lazarus forum
function IsDarkTheme: boolean;
function _Level(C: TColor): double;
begin
Result:= Red(C)*0.3 + Green(C)*0.59 + Blue(C)*0.11;
end;
begin
Result:= _Level(ColorToRGB(clWindow)) < _Level(ColorToRGB(clWindowText));
end;
Windows method
Example 1
// by "jwdietrich" from Lazarus forum
uses
Windows, Win32Proc, Registry;
// IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false)
function IsDarkTheme: boolean;
const
KEYPATH = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
KEYNAME = 'AppsUseLightTheme';
var
LightKey: boolean;
Registry: TRegistry;
begin
Result := false;
Registry := TRegistry.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
if Registry.OpenKeyReadOnly(KEYPATH) then
begin
if Registry.ValueExists(KEYNAME) then
LightKey := Registry.ReadBool(KEYNAME)
else
LightKey := true;
end
else
LightKey := true;
Result := not LightKey
finally
Registry.Free;
end;
end;
macOS method
Example 1
// by "trev" from Lazarus forum
{ returns true, if this app runs on macOS 10.14.0 Mojave or newer }
function MojaveOrNewer: boolean;
var
minOsVer: NSOperatingSystemVersion;
begin
//Setup minimum version (Mojave)
minOsVer.majorVersion:= 10;
minOsVer.minorVersion:= 14;
minOsVer.patchVersion:= 0;
// Check minimum version
if(NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer)) then
Result := True
else
Result := False;
end;
{ The following two functions were suggested by Hansaplast at https://forum.lazarus.freepascal.org/index.php/topic,43111.msg304366.html }
// Retrieve key's string value from user preferences. Result is encoded using NSStrToStr's default encoding.
function GetPrefString(const KeyName : string) : string;
begin
Result := NSStringToString(NSUserDefaults.standardUserDefaults.stringForKey(NSStr(@KeyName[1])));
end;
// IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false)
function IsDarkTheme: boolean;
begin
Result := false;
if MojaveOrNewer then
Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0;
end;
Demo
unit Unit1;
{$mode objfpc}{$H+}
{$modeswitch objectivec1}
interface
uses
Classes, Forms, Dialogs, StdCtrls, SysUtils,
CocoaAll, CocoaUtils, MacOSAll;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
{ returns true, if this app runs on macOS 10.14 Mojave or newer }
function MojaveOrNewer: boolean;
var
minOsVer: NSOperatingSystemVersion;
begin
// Setup minimum version (Mojave)
minOsVer.majorVersion:= 10;
minOsVer.minorVersion:= 14;
minOsVer.patchVersion:= 0;
// Check minimum version
if(NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer)) then
Result := True
else
Result := False;
end;
function GetPrefString(const KeyName : string) : string;
begin
Result := NSStringToString(NSUserDefaults.standardUserDefaults.stringForKey(NSStr(@KeyName[1])));
end;
function IsDarkTheme: boolean;
begin
Result := false;
if MojaveOrNewer then
Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if(IsDarkTheme) then
ShowMessage('Dark mode')
else
ShowMessage('Light mode');
end;
end.
Example 2
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.
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.
Tested on Mojave, Catalina, Big Sur.
// by "Clover" from Lazarus forum
function IsMacDarkMode: Boolean;
var
sMode: string;
begin
//sMode := CFStringToStr( CFStringRef( NSUserDefaults.StandardUserDefaults.stringForKey( NSSTR('AppleInterfaceStyle') ))); // Doesn't work in auto mode
sMode := CFStringToStr( CFStringRef( NSApp.effectiveAppearance.name ));
Result := Pos('Dark', sMode) > 0;
end;
Demo
unit Unit1;
{$mode objfpc}{$H+}
{$modeswitch objectivec1}
interface
uses
Classes, Forms, Dialogs, StdCtrls,
CocoaAll, CocoaUtils, MacOSAll;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
function IsMacDarkMode: Boolean;
var
sMode: string;
begin
sMode := CFStringToStr( CFStringRef( NSApp.effectiveAppearance.name ));
Result := Pos('Dark', sMode) > 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if(IsMacDarkMode) then
ShowMessage('Dark mode')
else
ShowMessage('Light mode');
end;
end.
Opting out of dark mode
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 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.
Opting in to dark mode
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 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.