Ignore:
Timestamp:
Dec 11, 2024, 10:26:02 AM (11 days ago)
Author:
chronos
Message:
  • Modified: Automatically detect and use dark mode on Windows.
  • Modified: Use dark mode title bar on Windows 10.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Packages/Common/Theme.pas

    r113 r115  
    55uses
    66  Classes, SysUtils, Graphics, ComCtrls, Controls, ExtCtrls, Menus, StdCtrls,
    7   Spin, Forms, Generics.Collections, Grids;
     7  Spin, Forms, Generics.Collections, Grids, Registry, LCLType;
    88
    99type
     
    3030  private
    3131    FTheme: TTheme;
     32    FActualTheme: TTheme;
    3233    function Gray(C: TColor): Byte;
    3334    procedure SetTheme(AValue: TTheme);
    34     procedure SetThemeName(AValue: TTheme);
     35    procedure SetThemeName(Name: string);
     36    procedure SetThemedTitleBar(AForm: TForm; Active: Bool);
     37    function IsWindows10OrGreater(BuildNumber: Integer): Boolean;
    3538  public
    3639    Used: Boolean;
     
    4245    procedure UseTheme(Form: TForm);
    4346    property Theme: TTheme read FTheme write SetTheme;
     47    property ActualTheme: TTheme read FActualTheme;
    4448  end;
    4549
     
    105109
    106110function TThemeManager.IsDarkTheme: Boolean;
    107 begin
    108   Result := Gray(ColorToRGB(clWindow)) < Gray(ColorToRGB(clWindowText));
    109 end;
    110 
    111 procedure TThemeManager.SetThemeName(AValue: TTheme);
     111{$IFDEF WINDOWS}
     112var
     113  LightKey: Boolean;
     114  Registry: TRegistry;
     115const
     116  KeyPath = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
     117  KeyName = 'AppsUseLightTheme';
     118{$ELSE}
     119var
     120  ColorWindow: TColor;
     121  ColorWindowText: TColor;
     122{$ENDIF}
     123begin
     124  Result := False;
     125  {$IFDEF WINDOWS}
     126  Registry := TRegistry.Create;
     127  try
     128    Registry.RootKey := HKEY_CURRENT_USER;
     129    if Registry.OpenKeyReadOnly(KeyPath) then begin
     130      if Registry.ValueExists(KeyName) then
     131        LightKey := Registry.ReadBool(KeyName)
     132      else LightKey := True;
     133    end else LightKey := True;
     134    Result := not LightKey;
     135  finally
     136    Registry.Free;
     137  end;
     138  {$ELSE}
     139  ColorWindow := ColorToRGB(clWindow);
     140  ColorWindowText := ColorToRGB(clWindowText);
     141  Result := Gray(ColorWindow) < Gray(ColorWindowText);
     142  {$ENDIF}
     143end;
     144
     145procedure TThemeManager.SetThemeName(Name: string);
     146begin
     147  Theme := Themes.FindByName(Name);
     148end;
     149
     150function TThemeManager.IsWindows10OrGreater(BuildNumber: Integer): Boolean;
     151begin
     152  {$IFDEF WINDOWS}
     153  Result := (Win32MajorVersion >= 10) and (Win32BuildNumber >= BuildNumber);
     154  {$ELSE}
     155  Result := False;
     156  {$ENDIF}
     157end;
     158
     159procedure TThemeManager.SetThemedTitleBar(AForm: TForm; Active: Bool);
     160type
     161  TDwmSetWindowAttribute = function(hwnd: HWND; dwAttribute: DWORD; pvAttribute: Pointer; cbAttribute: DWORD): HRESULT; stdcall;
     162const
     163  DwmapiLibName = 'dwmapi.dll';
     164  DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
     165  DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
     166var
     167  DwmapiLib: TLibHandle;
     168  DwmSetWindowAttribute: TDwmSetWindowAttribute;
     169  Attr: DWord;
     170begin
     171  DwmapiLib := LoadLibrary(DwmapiLibName);
     172  if DwmapiLib <> 0 then DwmSetWindowAttribute := GetProcAddress(DwmapiLib, 'DwmSetWindowAttribute')
     173    else DwmSetWindowAttribute := nil;
     174
     175  if Assigned(DwmSetWindowAttribute) and IsWindows10OrGreater(17763) then begin
     176    Attr := DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
     177    if IsWindows10OrGreater(18985) then Attr := DWMWA_USE_IMMERSIVE_DARK_MODE;
     178
     179    DwmSetWindowAttribute(AForm.Handle, Attr, @Active, SizeOf(Active));
     180  end;
     181  if DwmapiLib <> 0 then FreeLibrary(DwmapiLib);
     182end;
     183
     184procedure TThemeManager.SetTheme(AValue: TTheme);
    112185begin
    113186  if FTheme = AValue then Exit;
    114187  FTheme := AValue;
    115 end;
    116 
    117 procedure TThemeManager.SetTheme(AValue: TTheme);
    118 begin
    119   if FTheme = AValue then Exit;
    120   FTheme := AValue;
     188  FActualTheme := FTheme;
     189  {$IFDEF WINDOWS}
     190  if Assigned(FTheme) and (FTheme = Themes.FindByName(ThemeNameSystem)) and IsDarkTheme then
     191    FActualTheme := Themes.FindByName(ThemeNameDark);
     192  {$ENDIF}
    121193end;
    122194
     
    132204    ColorControlSelected := clWindow;
    133205  end;
    134   Theme := TTheme(Themes.First);
    135206  with Themes.AddNew(ThemeNameDark) do begin
    136207    ColorWindow := RGBToColor($20, $20, $20);
     
    147218    ColorControlSelected := RGBToColor(196, 225, 255);
    148219  end;
     220  Theme := TTheme(Themes.First);
    149221end;
    150222
     
    170242    (Control is TMemo) or (Control is TListView) or (Control is TCustomDrawGrid) or
    171243    (Control is TCheckBox) or (Control is TPageControl) or (Control is TRadioButton) then begin
    172       Control.Color := FTheme.ColorWindow;
    173       Control.Font.Color := FTheme.ColorWindowText;
     244      Control.Color := FActualTheme.ColorWindow;
     245      Control.Font.Color := FActualTheme.ColorWindowText;
    174246    end else begin
    175       Control.Color := FTheme.ColorControl;
    176       Control.Font.Color := FTheme.ColorControlText;
     247      Control.Color := FActualTheme.ColorControl;
     248      Control.Font.Color := FActualTheme.ColorControlText;
    177249    end;
    178250
    179251    if Control is TCustomDrawGrid then begin
    180       (Control as TCustomDrawGrid).Editor.Color := FTheme.ColorWindow;
    181       (Control as TCustomDrawGrid).Editor.Font.Color := FTheme.ColorWindowText;
     252      (Control as TCustomDrawGrid).Editor.Color := FActualTheme.ColorWindow;
     253      (Control as TCustomDrawGrid).Editor.Font.Color := FActualTheme.ColorWindowText;
    182254    end;
    183255
     
    195267procedure TThemeManager.UseTheme(Form: TForm);
    196268begin
    197   if not Used and (FTheme.Name = ThemeNameSystem) then Exit;
     269  if not Used and (FActualTheme.Name = ThemeNameSystem) then Exit;
    198270  ApplyTheme(Form);
     271  SetThemedTitleBar(Form, FActualTheme.Name = ThemeNameDark);
    199272  Used := True;
    200273end;
Note: See TracChangeset for help on using the changeset viewer.