Changeset 341


Ignore:
Timestamp:
Dec 21, 2024, 12:47:18 PM (22 hours ago)
Author:
chronos
Message:
  • Modified: Improved dark theme support on Windows.
Location:
trunk/Packages/Common
Files:
4 edited

Legend:

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

    r332 r341  
    5050    PersistentForm.Load(Self);
    5151    FullScreen := PersistentForm.FormFullScreen;
     52    ThemeManager.UseTheme(Self);
    5253  end;
    5354end;
     
    7576
    7677  Translator.TranslateComponentRecursive(Self);
    77   ThemeManager.UseTheme(Self);
    7878  Inc(FCounter);
    7979  inherited;
  • trunk/Packages/Common/MetaCanvas.pas

    r315 r341  
    150150    procedure SetWidth(AValue: Integer); override;
    151151    function GetWidth: Integer; override;
    152     procedure DoLine (x1,y1,x2,y2:integer); override;
     152    procedure DoLine(X1, Y1, X2, Y2: Integer); override;
    153153    procedure DoTextOut(X, Y: Integer; Text: string); override;
    154154    procedure DoRectangle(const Bounds: TRect); override;
     
    563563end;
    564564
    565 procedure TMetaCanvas.DoLine(x1, y1, x2, y2: integer);
     565procedure TMetaCanvas.DoLine(X1, Y1, X2, Y2: integer);
    566566var
    567567  NewObj: TCanvasLine;
  • trunk/Packages/Common/RegistryEx.pas

    r315 r341  
    3636    function ReadFloatWithDefault(const Name: string;
    3737      DefaultValue: Double): Double;
     38    function ReadDateTimeWithDefault(const Name: string; DefaultValue: TDateTime): TDateTime;
    3839    function DeleteKeyRecursive(const Key: string): Boolean;
    3940    function OpenKey(const Key: string; CanCreate: Boolean): Boolean;
     
    110111end;
    111112
     113function TRegistryEx.ReadDateTimeWithDefault(const Name: string;
     114  DefaultValue: TDateTime): TDateTime;
     115begin
     116  if ValueExists(Name) then Result := ReadDateTime(Name)
     117    else begin
     118      WriteDateTime(Name, DefaultValue);
     119      Result := DefaultValue;
     120    end;
     121end;
     122
    112123function TRegistryEx.DeleteKeyRecursive(const Key: string): Boolean;
    113124var
  • trunk/Packages/Common/Theme.pas

    r315 r341  
    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
     
    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;
     51    property ActualTheme: TTheme read FActualTheme;
    4252  end;
    4353
     
    4656  ThemeNameLight = 'Light';
    4757  ThemeNameDark = 'Dark';
     58  DwmapiLibName = 'dwmapi.dll';
     59  DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
     60  DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
    4861
    4962procedure Register;
     
    95108end;
    96109
    97 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);
    98179begin
    99180  if FTheme = AValue then Exit;
    100181  FTheme := AValue;
    101 end;
    102 
    103 procedure TThemeManager.SetTheme(AValue: TTheme);
    104 begin
    105   if FTheme = AValue then Exit;
    106   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}
    107187end;
    108188
     
    110190begin
    111191  inherited;
     192  {$IFDEF WINDOWS}
     193  DwmapiLib := LoadLibrary(DwmapiLibName);
     194  if DwmapiLib <> 0 then DwmSetWindowAttribute := GetProcAddress(DwmapiLib, 'DwmSetWindowAttribute')
     195    else DwmSetWindowAttribute := nil;
     196  {$ENDIF}
     197
    112198  Themes := TThemes.Create;
    113199  with Themes.AddNew(ThemeNameSystem) do begin
     
    118204    ColorControlSelected := clWindow;
    119205  end;
    120   Theme := TTheme(Themes.First);
    121206  with Themes.AddNew(ThemeNameDark) do begin
    122207    ColorWindow := RGBToColor($20, $20, $20);
     
    133218    ColorControlSelected := RGBToColor(196, 225, 255);
    134219  end;
     220  Theme := TTheme(Themes.First);
    135221end;
    136222
     
    138224begin
    139225  FreeAndNil(Themes);
     226  {$IFDEF WINDOWS}
     227  if DwmapiLib <> 0 then FreeLibrary(DwmapiLib);
     228  {$ENDIF}
    140229  inherited;
    141230end;
     
    156245    (Control is TMemo) or (Control is TListView) or (Control is TCustomDrawGrid) or
    157246    (Control is TCheckBox) or (Control is TPageControl) or (Control is TRadioButton) then begin
    158       Control.Color := FTheme.ColorWindow;
    159       Control.Font.Color := FTheme.ColorWindowText;
     247      Control.Color := FActualTheme.ColorWindow;
     248      Control.Font.Color := FActualTheme.ColorWindowText;
    160249    end else begin
    161       Control.Color := FTheme.ColorControl;
    162       Control.Font.Color := FTheme.ColorControlText;
     250      Control.Color := FActualTheme.ColorControl;
     251      Control.Font.Color := FActualTheme.ColorControlText;
    163252    end;
    164253
    165254    if Control is TCustomDrawGrid then begin
    166       (Control as TCustomDrawGrid).Editor.Color := FTheme.ColorWindow;
    167       (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;
    168257    end;
    169258
     
    181270procedure TThemeManager.UseTheme(Form: TForm);
    182271begin
    183   if not Used and (FTheme.Name = ThemeNameSystem) then Exit;
     272  if not Used and (FActualTheme.Name = ThemeNameSystem) then Exit;
    184273  ApplyTheme(Form);
     274  SetThemedTitleBar(Form, FActualTheme.Name = ThemeNameDark);
    185275  Used := True;
    186276end;
Note: See TracChangeset for help on using the changeset viewer.