Skip to content

Commit 816a819

Browse files
committed
feat: add Linux Qt theme selection
Linux Qt previously followed the system theme only. Add System, Light and Dark modes for Qt5 and Qt6 without affecting GTK, Windows or macOS.
1 parent 31f5c73 commit 816a819

5 files changed

Lines changed: 171 additions & 8 deletions

File tree

heidisql.lpi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,10 @@ if TargetOS='win64' then
437437
<Filename Value="source\lazaruscompat.pas"/>
438438
<IsPartOfProject Value="True"/>
439439
</Unit>
440+
<Unit>
441+
<Filename Value="source\platformtheme.pas"/>
442+
<IsPartOfProject Value="True"/>
443+
</Unit>
440444
<Unit>
441445
<Filename Value="source\crashdialog.pas"/>
442446
<IsPartOfProject Value="True"/>

heidisql.lpr

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
SysUtils, Dialogs,
1111
Forms, printer4lazarus, datetimectrls, LCLTranslator, Translations,
1212
{ you can add units after this }
13-
main, apphelpers, dbconnection, generic_types;
13+
main, apphelpers, dbconnection, generic_types
14+
{$if defined(LINUX) and (defined(LCLQt5) or defined(LCLQt6))}
15+
, platformtheme
16+
{$endif};
1417

1518
{$R *.res}
1619

1720
var
1821
AppLanguage: String;
19-
WasDarkMode: Boolean;
22+
WasDarkMode, IsDarkMode: Boolean;
2023
begin
2124
PostponedLogItems := TDBLogItems.Create(True);
2225
Application.{%H-}MainFormOnTaskBar := True; // hide warning: Symbol "MainFormOnTaskBar" is not portable
@@ -68,14 +71,20 @@
6871
{$ENDIF}
6972
// Switch synedit and grid colors to dark mode and vice versa
7073
WasDarkMode := AppSettings.ReadBool(asCurrentThemeIsDark);
71-
if (not WasDarkMode) and ThemeIsDark then
74+
IsDarkMode := ThemeIsDark;
75+
if (not WasDarkMode) and IsDarkMode then
7276
AppColorSchemes.ApplyDark
73-
else if WasDarkMode and (not ThemeIsDark) then
77+
else if WasDarkMode and (not IsDarkMode) then
7478
AppColorSchemes.ApplyLight;
75-
AppSettings.WriteBool(asCurrentThemeIsDark, ThemeIsDark);
79+
AppSettings.WriteBool(asCurrentThemeIsDark, IsDarkMode);
7680

7781
Application.Initialize;
7882

83+
{$if defined(LINUX) and (defined(LCLQt5) or defined(LCLQt6))}
84+
// Let LCL install its Qt event hook before replacing the application palette.
85+
ApplyPlatformTheme(AppSettings.ReadInt(asThemeMode));
86+
{$endif}
87+
7988
Application.CreateForm(TMainForm, MainForm);
8089
Application.OnException := MainForm.ApplicationException;
8190
MainForm.AfterFormCreate;

source/apphelpers.pas

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,10 @@ TAppSettings = class(TObject)
457457

458458
implementation
459459

460-
uses main;
460+
uses main
461+
{$if defined(LINUX) and (defined(LCLQt5) or defined(LCLQt6))}
462+
, platformtheme
463+
{$endif};
461464

462465

463466

@@ -2893,8 +2896,11 @@ function ThemeIsDark: Boolean;
28932896
Result := uDarkStyleParams.IsDarkModeEnabled;
28942897
{$ENDIF}
28952898
{$IFDEF LINUX}
2896-
// Not yet possible to detect the system's dark mode. Ideas welcome.
2899+
{$if defined(LCLQt5) or defined(LCLQt6)}
2900+
Result := PlatformThemeIsDark(AppSettings.ReadInt(asThemeMode));
2901+
{$else}
28972902
Result := False;
2903+
{$endif}
28982904
{$ENDIF}
28992905
{$IFDEF DARWIN}
29002906
// Detect system's dark mode on macOS

source/platformtheme.pas

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
unit platformtheme;
2+
3+
{$mode delphi}{$H+}
4+
5+
{$IFDEF LINUX}
6+
{$if defined(LCLQt5) or defined(LCLQt6)}
7+
{$DEFINE HEIDI_LINUX_QT_THEME}
8+
{$endif}
9+
{$ENDIF}
10+
11+
interface
12+
13+
procedure ApplyPlatformTheme(ThemeMode: Integer);
14+
function PlatformThemeIsDark(ThemeMode: Integer): Boolean;
15+
16+
implementation
17+
18+
{$IFDEF HEIDI_LINUX_QT_THEME}
19+
uses
20+
{$IFDEF LCLQt5}
21+
qt5;
22+
{$ELSE}
23+
qt6;
24+
{$ENDIF}
25+
26+
procedure SetPaletteRole(Palette: QPaletteH; var Color: TQColor;
27+
Role: QPaletteColorRole; R, G, B: Integer);
28+
begin
29+
QColor_fromRgb(@Color, R, G, B, 255);
30+
QPalette_setColor(Palette, Role, @Color);
31+
end;
32+
33+
procedure SetPaletteGroupRole(Palette: QPaletteH; var Color: TQColor;
34+
Group: QPaletteColorGroup; Role: QPaletteColorRole; R, G, B: Integer);
35+
begin
36+
QColor_fromRgb(@Color, R, G, B, 255);
37+
QPalette_setColor(Palette, Group, Role, @Color);
38+
end;
39+
40+
procedure ApplyLightPalette(Palette: QPaletteH; var Color: TQColor);
41+
begin
42+
SetPaletteRole(Palette, Color, QPaletteWindow, 239, 240, 241);
43+
SetPaletteRole(Palette, Color, QPaletteWindowText, 35, 38, 41);
44+
SetPaletteRole(Palette, Color, QPaletteBase, 255, 255, 255);
45+
SetPaletteRole(Palette, Color, QPaletteAlternateBase, 247, 247, 247);
46+
SetPaletteRole(Palette, Color, QPaletteText, 35, 38, 41);
47+
SetPaletteRole(Palette, Color, QPaletteButton, 239, 240, 241);
48+
SetPaletteRole(Palette, Color, QPaletteButtonText, 35, 38, 41);
49+
SetPaletteRole(Palette, Color, QPaletteToolTipBase, 255, 255, 220);
50+
SetPaletteRole(Palette, Color, QPaletteToolTipText, 35, 38, 41);
51+
SetPaletteRole(Palette, Color, QPaletteBrightText, 255, 255, 255);
52+
SetPaletteRole(Palette, Color, QPaletteLight, 255, 255, 255);
53+
SetPaletteRole(Palette, Color, QPaletteMidlight, 247, 247, 247);
54+
SetPaletteRole(Palette, Color, QPaletteMid, 200, 200, 200);
55+
SetPaletteRole(Palette, Color, QPaletteDark, 160, 160, 160);
56+
SetPaletteRole(Palette, Color, QPaletteShadow, 118, 118, 118);
57+
58+
SetPaletteGroupRole(Palette, Color, QPaletteDisabled, QPaletteWindowText, 120, 120, 120);
59+
SetPaletteGroupRole(Palette, Color, QPaletteDisabled, QPaletteText, 120, 120, 120);
60+
SetPaletteGroupRole(Palette, Color, QPaletteDisabled, QPaletteButtonText, 120, 120, 120);
61+
end;
62+
63+
procedure ApplyDarkPalette(Palette: QPaletteH; var Color: TQColor);
64+
begin
65+
SetPaletteRole(Palette, Color, QPaletteWindow, 49, 54, 59);
66+
SetPaletteRole(Palette, Color, QPaletteWindowText, 239, 240, 241);
67+
SetPaletteRole(Palette, Color, QPaletteBase, 35, 38, 41);
68+
SetPaletteRole(Palette, Color, QPaletteAlternateBase, 43, 48, 53);
69+
SetPaletteRole(Palette, Color, QPaletteText, 239, 240, 241);
70+
SetPaletteRole(Palette, Color, QPaletteButton, 49, 54, 59);
71+
SetPaletteRole(Palette, Color, QPaletteButtonText, 239, 240, 241);
72+
SetPaletteRole(Palette, Color, QPaletteToolTipBase, 49, 54, 59);
73+
SetPaletteRole(Palette, Color, QPaletteToolTipText, 239, 240, 241);
74+
SetPaletteRole(Palette, Color, QPaletteBrightText, 255, 255, 255);
75+
SetPaletteRole(Palette, Color, QPaletteLight, 91, 98, 104);
76+
SetPaletteRole(Palette, Color, QPaletteMidlight, 69, 75, 80);
77+
SetPaletteRole(Palette, Color, QPaletteMid, 37, 41, 44);
78+
SetPaletteRole(Palette, Color, QPaletteDark, 27, 30, 32);
79+
SetPaletteRole(Palette, Color, QPaletteShadow, 16, 18, 20);
80+
81+
SetPaletteGroupRole(Palette, Color, QPaletteDisabled, QPaletteWindowText, 130, 133, 136);
82+
SetPaletteGroupRole(Palette, Color, QPaletteDisabled, QPaletteText, 130, 133, 136);
83+
SetPaletteGroupRole(Palette, Color, QPaletteDisabled, QPaletteButtonText, 130, 133, 136);
84+
end;
85+
86+
procedure ApplyPlatformTheme(ThemeMode: Integer);
87+
var
88+
Palette: QPaletteH;
89+
Color: TQColor;
90+
begin
91+
if not (ThemeMode in [1, 2]) then
92+
Exit;
93+
94+
Palette := QPalette_Create;
95+
Color := Default(TQColor);
96+
try
97+
// Keep the current Qt palette as the base so accent and selection colors stay intact.
98+
QApplication_palette(Palette, QWidgetH(nil));
99+
if ThemeMode = 1 then
100+
ApplyLightPalette(Palette, Color)
101+
else
102+
ApplyDarkPalette(Palette, Color);
103+
QApplication_setPalette(Palette, nil);
104+
finally
105+
QPalette_Destroy(Palette);
106+
end;
107+
end;
108+
109+
function PlatformThemeIsDark(ThemeMode: Integer): Boolean;
110+
var
111+
Palette: QPaletteH;
112+
Color: PQColor;
113+
R, G, B: Integer;
114+
begin
115+
case ThemeMode of
116+
1: Exit(False);
117+
2: Exit(True);
118+
end;
119+
120+
Palette := QPalette_Create;
121+
try
122+
QApplication_palette(Palette, QWidgetH(nil));
123+
Color := QPalette_color(Palette, QPaletteActive, QPaletteWindow);
124+
QColor_getRgb(QColorH(Color), @R, @G, @B);
125+
Result := (R * 299 + G * 587 + B * 114) < 128000;
126+
finally
127+
QPalette_Destroy(Palette);
128+
end;
129+
end;
130+
131+
{$ELSE}
132+
133+
procedure ApplyPlatformTheme(ThemeMode: Integer);
134+
begin
135+
end;
136+
137+
function PlatformThemeIsDark(ThemeMode: Integer): Boolean;
138+
begin
139+
Result := False;
140+
end;
141+
142+
{$ENDIF}
143+
144+
end.

source/preferences.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ procedure TfrmPreferences.FormCreate(Sender: TObject);
563563
comboGUIFont.Items.Add(Screen.Fonts[i]);
564564
end;
565565

566-
{$IFDEF WINDOWS}
566+
{$if defined(WINDOWS) or (defined(LINUX) and (defined(LCLQt5) or defined(LCLQt6)))}
567567
comboTheme.Items.Add(_('Automatic, depending on system settings'));
568568
comboTheme.Items.Add(_('Light'));
569569
comboTheme.Items.Add(_('Dark'));

0 commit comments

Comments
 (0)