Initial commit

This commit is contained in:
alinvip22@gmail.com
2023-04-07 11:04:43 +05:00
commit cccf837dd3
9 changed files with 622 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+69
View File
@@ -0,0 +1,69 @@
# Uncomment these types if you want even more clean repository. But be careful.
# It can make harm to an existing project source. Read explanations below.
#
# Resource files are binaries containing manifest, project icon and version info.
# They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
#*.res
#
# Type library file (binary). In old Delphi versions it should be stored.
# Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
#*.tlb
#
# Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
# Uncomment this if you are not using diagrams or use newer Delphi version.
#*.ddp
#
# Visual LiveBindings file. Added in Delphi XE2.
# Uncomment this if you are not using LiveBindings Designer.
#*.vlb
#
# Deployment Manager configuration file for your project. Added in Delphi XE2.
# Uncomment this if it is not mobile development and you do not use remote debug feature.
#*.deployproj
#
# C++ object files produced when C/C++ Output file generation is configured.
# Uncomment this if you are not using external objects (zlib library for example).
#*.obj
#
# Delphi compiler-generated binaries (safe to delete)
*.exe
*.dll
*.bpl
*.bpi
*.dcp
*.so
*.apk
*.drc
*.map
*.dres
*.rsm
*.tds
*.dcu
*.lib
*.a
*.o
*.ocx
# Delphi autogenerated files (duplicated info)
*.cfg
*.hpp
*Resource.rc
# Delphi local files (user-specific info)
*.local
*.identcache
*.projdata
*.tvsconfig
*.dsk
# Delphi history and backups
__history/
__recovery/
*.~*
# Castalia statistics file (since XE7 Castalia is distributed with Delphi)
*.stat
# Boss dependency manager vendor folder https://github.com/HashLoad/boss
modules/
+35
View File
@@ -0,0 +1,35 @@
unit DarkModeApi.Consts;
interface
{$IFDEF MSWINDOWS}
uses
System.UITypes;
const
BackColor: TColor = $1E1E1E;
TextColor: TColor = $F0F0F0;
InputBackColor: TColor = $303030;
Dwmapi = 'DWMAPI.DLL';
CDarkModeExplorer = 'DarkMode_Explorer';
CModeExplorer = 'Explorer';
CDarkModeControlCFD = 'DarkMode_CFD';
DWM_CLOAKED_APP = $0000001;
DWM_CLOAKED_SHELL = $0000002;
DWM_CLOAKED_INHERITED = $0000004;
ODS_NOACCEL = $0100;
WM_UAHDESTROYWINDOW = $0090; // handled by DefWindowProc
WM_UAHDRAWMENU = $0091; // lParam is UAHMENU
WM_UAHDRAWMENUITEM = $0092; // lParam is UAHDRAWMENUITEM
WM_UAHINITMENU = $0093; // handled by DefWindowProc
WM_UAHMEASUREMENUITEM = $0094; // lParam is UAHMEASUREMENUITEM
WM_UAHNCPAINTMENUPOPUP = $0095; // handled by DefWindowProc
WM_UAHUPDATE = $0096;
{$ENDIF}
implementation
end.
+75
View File
@@ -0,0 +1,75 @@
unit DarkModeApi.FMX;
{$IFDEF MSWINDOWS}
interface
uses
FMX.Forms, FMX.Types;
type
TFormHelper = class helper for TForm
procedure SetWindowColorModeAsSystem;
procedure SetWindowColorMode(IsDark: Boolean);
class function SystemIsDarkMode: Boolean;
end;
procedure SetWindowColorModeAsSystemFor(Handle: TWindowHandle);
procedure SetWindowColorModeFor(Handle: TWindowHandle; IsDark: Boolean);
implementation
uses
DarkModeApi, FMX.Platform.Win, Winapi.Windows;
procedure SetWindowColorModeAsSystemFor(Handle: TWindowHandle);
var
Value: LongBool;
WinHandle: HWND;
begin
Value := IsDarkMode;
WinHandle := FmxHandleToHWND(Handle);
DwmSetWindowAttribute(WinHandle, ImmersiveDarkMode, Value, SizeOf(Value));
AllowDarkModeForWindow(WinHandle, Value);
AllowDarkModeForApp(Value);
end;
procedure SetWindowColorModeFor(Handle: TWindowHandle; IsDark: Boolean);
var
Value: LongBool;
WinHandle: HWND;
begin
Value := IsDark;
WinHandle := FmxHandleToHWND(Handle);
DwmSetWindowAttribute(WinHandle, ImmersiveDarkMode, Value, SizeOf(Value));
AllowDarkModeForWindow(WinHandle, Value);
AllowDarkModeForApp(Value);
end;
{ TFormHelper }
procedure TFormHelper.SetWindowColorMode(IsDark: Boolean);
begin
SetWindowColorModeFor(handle, IsDark);
end;
procedure TFormHelper.SetWindowColorModeAsSystem;
begin
SetWindowColorModeAsSystemFor(Handle);
end;
class function TFormHelper.SystemIsDarkMode: Boolean;
begin
Result := IsDarkMode;
end;
{$ELSE}
interface
implementation
{$ENDIF}
end.
+101
View File
@@ -0,0 +1,101 @@
unit DarkModeApi.Types;
{$IFDEF MSWINDOWS}
{$ALIGN ON}
{$MINENUMSIZE 4}
{$WEAKPACKAGEUNIT}
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Winapi.Windows;
type
TWinRoundType = (wrtDEFAULT = 0, wrtDONOTROUND = 1, wrtROUND = 2, wrtROUNDSMALL = 3);
TDwmWindowAttribute = (DWMWA_NCRENDERING_ENABLED = 1, DWMWA_NCRENDERING_POLICY, DWMWA_TRANSITIONS_FORCEDISABLED, DWMWA_ALLOW_NCPAINT, DWMWA_CAPTION_BUTTON_BOUNDS, DWMWA_NONCLIENT_RTL_LAYOUT, DWMWA_FORCE_ICONIC_REPRESENTATION, DWMWA_FLIP3D_POLICY, DWMWA_EXTENDED_FRAME_BOUNDS, DWMWA_HAS_ICONIC_BITMAP, DWMWA_DISALLOW_PEEK, DWMWA_EXCLUDED_FROM_PEEK, DWMWA_CLOAK, DWMWA_CLOAKED, DWMWA_FREEZE_REPRESENTATION, DWMWA_PASSIVE_UPDATE_MODE, DWMWA_USE_HOSTBACKDROPBRUSH, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, DWMWA_USE_IMMERSIVE_DARK_MODE = 20, DWMWA_WINDOW_CORNER_PREFERENCE = 33, DWMWA_BORDER_COLOR, DWMWA_CAPTION_COLOR, DWMWA_TEXT_COLOR, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, DWMWA_SYSTEMBACKDROP_TYPE, DWMWA_LAST);
TDWMWindowCornerPreference = (DWMWCP_DEFAULT = 0, DWMWCP_DONOTROUND = 1, DWMWCP_ROUND = 2, DWMWCP_ROUNDSMALL = 3);
TImmersiveHCCacheMode = (IHCM_USE_CACHED_VALUE, IHCM_REFRESH);
TPreferredAppMode = (DefaultMode, AllowDarkMode, ForceDarkMode, ForceLightMode, ModeMax);
TWindowCompositionAttribute = (WCA_UNDEFINED = 0, //
WCA_NCRENDERING_ENABLED = 1, //
WCA_NCRENDERING_POLICY = 2, //
WCA_TRANSITIONS_FORCEDISABLED = 3, //
WCA_ALLOW_NCPAINT = 4, //
WCA_CAPTION_BUTTON_BOUNDS = 5, //
WCA_NONCLIENT_RTL_LAYOUT = 6, //
WCA_FORCE_ICONIC_REPRESENTATION = 7, //
WCA_EXTENDED_FRAME_BOUNDS = 8, //
WCA_HAS_ICONIC_BITMAP = 9, //
WCA_THEME_ATTRIBUTES = 10, //
WCA_NCRENDERING_EXILED = 11, //
WCA_NCADORNMENTINFO = 12, //
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, //
WCA_VIDEO_OVERLAY_ACTIVE = 14, //
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, //
WCA_DISALLOW_PEEK = 16, //
WCA_CLOAK = 17, //
WCA_CLOAKED = 18, //
WCA_ACCENT_POLICY = 19, //
WCA_FREEZE_REPRESENTATION = 20, //
WCA_EVER_UNCLOAKED = 21, //
WCA_VISUAL_OWNER = 22, //
WCA_HOLOGRAPHIC = 23, //
WCA_EXCLUDED_FROM_DDA = 24, //
WCA_PASSIVEUPDATEMODE = 25, //
WCA_USEDARKMODECOLORS = 26, //
WCA_LAST = 27);
TWindowCompositionAttrib = TWindowCompositionAttribute;
WINDOWCOMPOSITIONATTRIBDATA = record
Attrib: TWindowCompositionAttribute;
pvData: Pointer;
cbData: SIZE_T;
end;
TWindowCompositionAttribData = WINDOWCOMPOSITIONATTRIBDATA;
PWindowCompositionAttribData = ^TWindowCompositionAttribData;
TRtlGetNtVersionNumbers = procedure(var major, minor, build: DWORD); stdcall;
TSetWindowCompositionAttribute = function(hWnd: HWND; pData: PWindowCompositionAttribData): BOOL; stdcall;
TShouldAppsUseDarkMode = function: BOOL; stdcall;
TAllowDarkModeForWindow = function(hWnd: HWND; allow: BOOL): BOOL; stdcall;
TAllowDarkModeForApp = function(allow: BOOL): BOOL; stdcall;
TRefreshImmersiveColorPolicyState = procedure; stdcall;
TIsDarkModeAllowedForWindow = function(hWnd: HWND): BOOL; stdcall;
TGetIsImmersiveColorUsingHighContrast = function(mode: TImmersiveHCCacheMode): BOOL; stdcall;
TOpenNcThemeData = function(hWnd: HWND; pszClassList: LPCWSTR): THandle; stdcall;
TShouldSystemUseDarkMode = function: BOOL; stdcall;
TSetPreferredAppMode = function(appMode: TPreferredAppMode): TPreferredAppMode; stdcall;
TIsDarkModeAllowedForApp = function: BOOL; stdcall;
implementation
{$ELSE}
interface
implementation
{$ENDIF}
end.
+62
View File
@@ -0,0 +1,62 @@
unit DarkModeApi.Vcl;
interface
uses
Vcl.Forms;
type
TFormHelper = class helper for TForm
procedure SetWindowColorModeAsSystem;
procedure SetWindowColorMode(IsDark: Boolean);
class function SystemIsDarkMode: Boolean;
end;
procedure SetWindowColorModeAsSystemFor(Handle: THandle);
procedure SetWindowColorModeFor(Handle: THandle; IsDark: Boolean);
implementation
uses
DarkModeApi, FMX.Platform.Win, Winapi.Windows;
procedure SetWindowColorModeAsSystemFor(Handle: THandle);
var
Value: LongBool;
begin
Value := IsDarkMode;
DwmSetWindowAttribute(Handle, ImmersiveDarkMode, Value, SizeOf(Value));
AllowDarkModeForWindow(Handle, Value);
AllowDarkModeForApp(Value);
end;
procedure SetWindowColorModeFor(Handle: THandle; IsDark: Boolean);
var
Value: LongBool;
begin
Value := IsDark;
DwmSetWindowAttribute(Handle, ImmersiveDarkMode, Value, SizeOf(Value));
AllowDarkModeForWindow(Handle, Value);
AllowDarkModeForApp(Value);
end;
{ TFormHelper }
procedure TFormHelper.SetWindowColorMode(IsDark: Boolean);
begin
SetWindowColorModeFor(handle, IsDark);
end;
procedure TFormHelper.SetWindowColorModeAsSystem;
begin
SetWindowColorModeAsSystemFor(Handle);
end;
class function TFormHelper.SystemIsDarkMode: Boolean;
begin
Result := IsDarkMode;
end;
end.
+255
View File
@@ -0,0 +1,255 @@
unit DarkModeApi;
{$IFDEF MSWINDOWS}
{$WARN SYMBOL_PLATFORM OFF}
interface
// See also https://github.com/adzm/win32-custom-menubar-aero-theme
uses
Winapi.Windows, DarkModeApi.Types;
function DwmSetWindowAttribute(hwnd: HWND; dwAttribute: DWORD; pvAttribute: Pointer; cbAttribute: DWORD): HResult; stdcall; overload;
function DwmSetWindowAttribute(hwnd: HWND; dwAttribute: TDwmWindowAttribute; var pvAttribute; cbAttribute: DWORD): HResult; stdcall; overload;
function DwmSetWindowAttribute(hwnd: HWND; dwAttribute: TDwmWindowAttribute; var pvAttribute: TDWMWindowCornerPreference; cbAttribute: DWORD): HResult; stdcall; overload;
/// <summary>
/// Enables dark context menus which change automatically depending on the theme.
/// </summary>
procedure AllowDarkModeForApp(allow: BOOL); stdcall;
function AllowDarkModeForWindow(hWnd: HWND; allow: Boolean): Boolean; stdcall;
// See https://en.wikipedia.org/wiki/Windows_10_version_history
function CheckBuildNumber(buildNumber: DWORD): Boolean;
function IsWindows10OrGreater(buildNumber: DWORD = 10000): Boolean;
function IsWindows11OrGreater(buildNumber: DWORD = 22000): Boolean;
function IsDarkModeAllowedForWindow(hWnd: HWND): BOOL; stdcall;
procedure RefreshImmersiveColorPolicyState; stdcall;
procedure RefreshTitleBarThemeColor(hWnd: HWND);
function ShouldAppsUseDarkMode: BOOL; stdcall;
function ShouldSystemUseDarkMode: BOOL; stdcall;
function ImmersiveDarkMode: TDwmWindowAttribute;
/// <summary>
/// Checks the system registry to see if Dark mode is enabled
/// </summary>
function IsDarkMode: Boolean;
const
LOAD_LIBRARY_SEARCH_SYSTEM32 = $00000800;
implementation
uses
System.Classes, System.SysUtils, DarkModeApi.Consts, System.Win.Registry;
var
_AllowDarkModeForApp: TAllowDarkModeForApp = nil;
_AllowDarkModeForWindow: TAllowDarkModeForWindow = nil;
_GetIsImmersiveColorUsingHighContrast: TGetIsImmersiveColorUsingHighContrast = nil;
_IsDarkModeAllowedForWindow: TIsDarkModeAllowedForWindow = nil;
_OpenNcThemeData: TOpenNcThemeData = nil;
_RefreshImmersiveColorPolicyState: TRefreshImmersiveColorPolicyState = nil;
_SetPreferredAppMode: TSetPreferredAppMode = nil;
_SetWindowCompositionAttribute: TSetWindowCompositionAttribute = nil;
_ShouldAppsUseDarkMode: TShouldAppsUseDarkMode = nil;
_ShouldSystemUseDarkMode: TShouldSystemUseDarkMode = nil;
GDarkModeSupported: BOOL = False; // changed type to BOOL
GDarkModeEnabled: BOOL = False;
GUxTheme: HMODULE = 0;
function DwmSetWindowAttribute(hwnd: hwnd; dwAttribute: DWORD; pvAttribute: Pointer; cbAttribute: DWORD): HResult; stdcall; overload; external Dwmapi name 'DwmSetWindowAttribute' delayed;
function DwmSetWindowAttribute(hwnd: hwnd; dwAttribute: TDwmWindowAttribute; var pvAttribute: TDWMWindowCornerPreference; cbAttribute: DWORD): HResult; stdcall; overload; external Dwmapi name 'DwmSetWindowAttribute' delayed;
procedure AllowDarkModeForApp(allow: BOOL);
begin
if Assigned(_AllowDarkModeForApp) then
_AllowDarkModeForApp(allow)
else if Assigned(_SetPreferredAppMode) then
begin
if allow then
_SetPreferredAppMode(TPreferredAppMode.AllowDarkMode)
else
_SetPreferredAppMode(TPreferredAppMode.DefaultMode);
end;
end;
function DwmSetWindowAttribute(hwnd: hwnd; dwAttribute: TDwmWindowAttribute; var pvAttribute; cbAttribute: DWORD): HResult;
begin
Result := DwmSetWindowAttribute(hwnd, Ord(dwAttribute), @pvAttribute, cbAttribute);
end;
function IsDarkModeAllowedForWindow(hWnd: hWnd): BOOL;
begin
Result := Assigned(_IsDarkModeAllowedForWindow) and _IsDarkModeAllowedForWindow(hWnd);
end;
function GetIsImmersiveColorUsingHighContrast(mode: TImmersiveHCCacheMode): BOOL;
begin
Result := Assigned(_GetIsImmersiveColorUsingHighContrast) and _GetIsImmersiveColorUsingHighContrast(mode);
end;
function ImmersiveDarkMode: TDwmWindowAttribute;
begin
if IsWindows10OrGreater(18985) then
Result := DWMWA_USE_IMMERSIVE_DARK_MODE
else
Result := DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
end;
procedure RefreshImmersiveColorPolicyState;
begin
if Assigned(_RefreshImmersiveColorPolicyState) then
_RefreshImmersiveColorPolicyState;
end;
function IsDarkMode: Boolean;
var
LRegistry: TRegistry;
begin
LRegistry := TRegistry.Create;
try
LRegistry.RootKey := HKEY_CURRENT_USER;
LRegistry.OpenKeyReadOnly('\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize');
Result := not LRegistry.ReadBool('AppsUseLightTheme');
finally
LRegistry.Free;
end;
end;
function ShouldSystemUseDarkMode: BOOL;
begin
Result := Assigned(_ShouldSystemUseDarkMode) and _ShouldSystemUseDarkMode;
end;
function CheckBuildNumber(buildNumber: DWORD): Boolean;
begin
Result :=
IsWindows10OrGreater(20348) or
IsWindows10OrGreater(19045) or //
IsWindows10OrGreater(19044) or //
IsWindows10OrGreater(19043) or //
IsWindows10OrGreater(19042) or //
IsWindows10OrGreater(19041) or // 2004
IsWindows10OrGreater(18363) or // 1909
IsWindows10OrGreater(18362) or // 1903
IsWindows10OrGreater(17763); // 1809
end;
function IsWindows10OrGreater(buildNumber: DWORD): Boolean;
begin
Result := (TOSVersion.Major > 10) or ((TOSVersion.Major = 10) and (TOSVersion.Minor = 0) and (DWORD(TOSVersion.Build) >= buildNumber));
end;
function IsWindows11OrGreater(buildNumber: DWORD): Boolean;
begin
Result := IsWindows10OrGreater(22000) or IsWindows10OrGreater(buildNumber);
end;
function AllowDarkModeForWindow(hWnd: hWnd; allow: Boolean): Boolean;
begin
Result := GDarkModeSupported and _AllowDarkModeForWindow(hWnd, allow);
end;
function IsHighContrast: Boolean;
var
highContrast: HIGHCONTRASTW;
begin
highContrast.cbSize := SizeOf(highContrast);
if SystemParametersInfo(SPI_GETHIGHCONTRAST, SizeOf(highContrast), @highContrast, Ord(False)) then
Result := highContrast.dwFlags and HCF_HIGHCONTRASTON <> 0
else
Result := False;
end;
procedure RefreshTitleBarThemeColor(hWnd: hWnd);
var
LUseDark: BOOL;
LData: TWindowCompositionAttribData;
begin
LUseDark := _IsDarkModeAllowedForWindow(hWnd) and _ShouldAppsUseDarkMode and not IsHighContrast;
if TOSVersion.Build < 18362 then
SetProp(hWnd, 'UseImmersiveDarkModeColors', THandle(LUseDark))
else if Assigned(_SetWindowCompositionAttribute) then
begin
LData.Attrib := WCA_USEDARKMODECOLORS;
LData.pvData := @LUseDark;
LData.cbData := SizeOf(LUseDark);
_SetWindowCompositionAttribute(hWnd, @LData);
end;
end;
function ShouldAppsUseDarkMode: BOOL;
begin
Result := Assigned(_ShouldAppsUseDarkMode) and _ShouldAppsUseDarkMode;
end;
procedure InitDarkMode;
begin
if ((TOSVersion.Major = 10) and (TOSVersion.Minor = 0) and CheckBuildNumber(TOSVersion.Build)) then
begin
GUxTheme := LoadLibraryEx('uxtheme.dll', 0, LOAD_LIBRARY_SEARCH_SYSTEM32);
if GUxTheme <> 0 then
begin
@_AllowDarkModeForWindow := GetProcAddress(GUxTheme, MAKEINTRESOURCEA(133));
@_GetIsImmersiveColorUsingHighContrast := GetProcAddress(GUxTheme, MAKEINTRESOURCEA(106));
@_IsDarkModeAllowedForWindow := GetProcAddress(GUxTheme, MAKEINTRESOURCEA(137));
@_RefreshImmersiveColorPolicyState := GetProcAddress(GUxTheme, MAKEINTRESOURCEA(104));
@_SetWindowCompositionAttribute := GetProcAddress(GetModuleHandle(user32), 'SetWindowCompositionAttribute');
@_ShouldAppsUseDarkMode := GetProcAddress(GUxTheme, MAKEINTRESOURCEA(132));
var P := GetProcAddress(GUxTheme, MAKEINTRESOURCEA(135));
if TOSVersion.Build < 18362 then
@_AllowDarkModeForApp := P
else
@_SetPreferredAppMode := P;
if Assigned(_RefreshImmersiveColorPolicyState) and
Assigned(_ShouldAppsUseDarkMode) and Assigned(_AllowDarkModeForWindow) and
(Assigned(_AllowDarkModeForApp) or Assigned(_SetPreferredAppMode)) and
Assigned(_IsDarkModeAllowedForWindow) then
begin
GDarkModeSupported := True;
AllowDarkModeForApp(True);
_RefreshImmersiveColorPolicyState;
GDarkModeEnabled := ShouldAppsUseDarkMode and not IsHighContrast;
end;
end;
end;
end;
procedure DoneDarkMode;
begin
if GUxTheme <> 0 then
FreeLibrary(GUxTheme);
end;
initialization
InitDarkMode;
finalization
DoneDarkMode;
{$ELSE}
interface
implementation
{$ENDIF}
end.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 alinvip22@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2
View File
@@ -0,0 +1,2 @@
# WindowDarkMode
Dark mode for Window frame (Win only)