Ignore:
Timestamp:
Jan 17, 2025, 9:05:54 PM (4 days ago)
Author:
chronos
Message:
  • Modified: Updated Common package.
  • Modified: Remove U prefix from unit names.
  • Modified: Use Gneeric.Collections instead of fgl.
  • Modified: Do not use global form variables.
File:
1 moved

Legend:

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

    r218 r219  
    1 unit UTheme;
     1unit Theme;
    22
    33interface
     
    55uses
    66  Classes, SysUtils, Graphics, ComCtrls, Controls, ExtCtrls, Menus, StdCtrls,
    7   Spin, Forms, fgl, Grids;
     7  Spin, Forms, Generics.Collections, Grids, Registry, LCLType;
    88
    99type
     
    1919  { TThemes }
    2020
    21   TThemes = class(TFPGObjectList<TTheme>)
     21  TThemes = class(TObjectList<TTheme>)
    2222    function AddNew(Name: string): TTheme;
    2323    function FindByName(Name: string): TTheme;
     
    2525  end;
    2626
     27  TDwmSetWindowAttribute = function(hwnd: HWND; dwAttribute: DWORD; pvAttribute: Pointer; cbAttribute: DWORD): HRESULT; stdcall;
     28
    2729  { TThemeManager }
    2830
     
    3032  private
    3133    FTheme: TTheme;
     34    FActualTheme: TTheme;
     35    DwmapiLib: TLibHandle;
     36    DwmSetWindowAttribute: TDwmSetWindowAttribute;
     37    function Gray(C: TColor): Byte;
    3238    procedure SetTheme(AValue: TTheme);
    33     procedure SetThemeName(AValue: TTheme);
     39    procedure SetThemeName(Name: string);
     40    procedure SetThemedTitleBar(AForm: TForm; Active: Bool);
     41    function IsWindows10OrGreater(BuildNumber: Integer): Boolean;
    3442  public
    3543    Used: Boolean;
    3644    Themes: TThemes;
     45    function IsDarkTheme: Boolean;
    3746    procedure ApplyTheme(Component: TComponent);
    3847    constructor Create(AOwner: TComponent); override;
     
    4049    procedure UseTheme(Form: TForm);
    4150    property Theme: TTheme read FTheme write SetTheme;
    42   end;
     51    property ActualTheme: TTheme read FActualTheme;
     52  end;
     53
     54const
     55  ThemeNameSystem = 'System';
     56  ThemeNameLight = 'Light';
     57  ThemeNameDark = 'Dark';
     58  DwmapiLibName = 'dwmapi.dll';
     59  DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
     60  DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
    4361
    4462procedure Register;
     63
    4564
    4665implementation
     
    89108end;
    90109
    91 procedure TThemeManager.SetThemeName(AValue: TTheme);
     110{ TThemeManager }
     111
     112function TThemeManager.Gray(C: TColor): Byte;
     113begin
     114  Result := Trunc(Red(C) * 0.3 + Green(C) * 0.59 + Blue(C) * 0.11);
     115end;
     116
     117function TThemeManager.IsDarkTheme: Boolean;
     118{$IFDEF WINDOWS}
     119var
     120  LightKey: Boolean;
     121  Registry: TRegistry;
     122const
     123  KeyPath = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
     124  KeyName = 'AppsUseLightTheme';
     125{$ELSE}
     126var
     127  ColorWindow: TColor;
     128  ColorWindowText: TColor;
     129{$ENDIF}
     130begin
     131  Result := False;
     132  {$IFDEF WINDOWS}
     133  Registry := TRegistry.Create;
     134  try
     135    Registry.RootKey := HKEY_CURRENT_USER;
     136    if Registry.OpenKeyReadOnly(KeyPath) then begin
     137      if Registry.ValueExists(KeyName) then
     138        LightKey := Registry.ReadBool(KeyName)
     139      else LightKey := True;
     140    end else LightKey := True;
     141    Result := not LightKey;
     142  finally
     143    Registry.Free;
     144  end;
     145  {$ELSE}
     146  ColorWindow := ColorToRGB(clWindow);
     147  ColorWindowText := ColorToRGB(clWindowText);
     148  Result := Gray(ColorWindow) < Gray(ColorWindowText);
     149  {$ENDIF}
     150end;
     151
     152procedure TThemeManager.SetThemeName(Name: string);
     153begin
     154  Theme := Themes.FindByName(Name);
     155end;
     156
     157function TThemeManager.IsWindows10OrGreater(BuildNumber: Integer): Boolean;
     158begin
     159  {$IFDEF WINDOWS}
     160  Result := (Win32MajorVersion >= 10) and (Win32BuildNumber >= BuildNumber);
     161  {$ELSE}
     162  Result := False;
     163  {$ENDIF}
     164end;
     165
     166procedure TThemeManager.SetThemedTitleBar(AForm: TForm; Active: Bool);
     167var
     168  Attr: DWord;
     169begin
     170  if Assigned(DwmSetWindowAttribute) and IsWindows10OrGreater(17763) then begin
     171    Attr := DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
     172    if IsWindows10OrGreater(18985) then Attr := DWMWA_USE_IMMERSIVE_DARK_MODE;
     173
     174    DwmSetWindowAttribute(AForm.Handle, Attr, @Active, SizeOf(Active));
     175  end;
     176end;
     177
     178procedure TThemeManager.SetTheme(AValue: TTheme);
    92179begin
    93180  if FTheme = AValue then Exit;
    94181  FTheme := AValue;
    95 end;
    96 
    97 procedure TThemeManager.SetTheme(AValue: TTheme);
    98 begin
    99   if FTheme = AValue then Exit;
    100   FTheme := AValue;
     182  FActualTheme := FTheme;
     183  {$IFDEF WINDOWS}
     184  if Assigned(FTheme) and (FTheme = Themes.FindByName(ThemeNameSystem)) and IsDarkTheme then
     185    FActualTheme := Themes.FindByName(ThemeNameDark);
     186  {$ENDIF}
    101187end;
    102188
     
    104190begin
    105191  inherited;
     192  {$IFDEF WINDOWS}
     193  DwmapiLib := LoadLibrary(DwmapiLibName);
     194  if DwmapiLib <> 0 then DwmSetWindowAttribute := GetProcAddress(DwmapiLib, 'DwmSetWindowAttribute')
     195    else DwmSetWindowAttribute := nil;
     196  {$ENDIF}
     197
    106198  Themes := TThemes.Create;
    107   with Themes.AddNew('System') do begin
     199  with Themes.AddNew(ThemeNameSystem) do begin
    108200    ColorWindow := clWindow;
    109201    ColorWindowText := clWindowText;
     
    112204    ColorControlSelected := clWindow;
    113205  end;
    114   Theme := TTheme(Themes.First);
    115   with Themes.AddNew('Dark') do begin
     206  with Themes.AddNew(ThemeNameDark) do begin
    116207    ColorWindow := RGBToColor($20, $20, $20);
    117208    ColorWindowText := clWhite;
     
    120211    ColorControlSelected := RGBToColor(96, 125, 155);
    121212  end;
    122   with Themes.AddNew('Light') do begin
     213  with Themes.AddNew(ThemeNameLight) do begin
    123214    ColorWindow := clWhite;
    124215    ColorWindowText := clBlack;
     
    127218    ColorControlSelected := RGBToColor(196, 225, 255);
    128219  end;
     220  Theme := TTheme(Themes.First);
    129221end;
    130222
     
    132224begin
    133225  FreeAndNil(Themes);
     226  {$IFDEF WINDOWS}
     227  if DwmapiLib <> 0 then FreeLibrary(DwmapiLib);
     228  {$ENDIF}
    134229  inherited;
    135230end;
     
    150245    (Control is TMemo) or (Control is TListView) or (Control is TCustomDrawGrid) or
    151246    (Control is TCheckBox) or (Control is TPageControl) or (Control is TRadioButton) then begin
    152       Control.Color := FTheme.ColorWindow;
    153       Control.Font.Color := FTheme.ColorWindowText;
     247      Control.Color := FActualTheme.ColorWindow;
     248      Control.Font.Color := FActualTheme.ColorWindowText;
    154249    end else begin
    155       Control.Color := FTheme.ColorControl;
    156       Control.Font.Color := FTheme.ColorControlText;
     250      Control.Color := FActualTheme.ColorControl;
     251      Control.Font.Color := FActualTheme.ColorControlText;
    157252    end;
    158253
    159254    if Control is TCustomDrawGrid then begin
    160       (Control as TCustomDrawGrid).Editor.Color := FTheme.ColorWindow;
    161       (Control as TCustomDrawGrid).Editor.Font.Color := FTheme.ColorWindowText;
     255      (Control as TCustomDrawGrid).Editor.Color := FActualTheme.ColorWindow;
     256      (Control as TCustomDrawGrid).Editor.Font.Color := FActualTheme.ColorWindowText;
    162257    end;
    163258
     
    175270procedure TThemeManager.UseTheme(Form: TForm);
    176271begin
    177   if not Used and (FTheme.Name = 'System') then Exit;
     272  if not Used and (FActualTheme.Name = ThemeNameSystem) then Exit;
    178273  ApplyTheme(Form);
     274  SetThemedTitleBar(Form, FActualTheme.Name = ThemeNameDark);
    179275  Used := True;
    180276end;
    181277
    182 
    183278end.
Note: See TracChangeset for help on using the changeset viewer.