udarktheme.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UDarkTheme;
  3. {$mode objfpc}{$H+}
  4. {$IF defined(DARWIN) and defined(CPU64)}{$DEFINE DARWIN_DARK_THEME}{$ENDIF}
  5. {$IFDEF DARWIN_DARK_THEME}{$modeswitch objectivec1}{$ENDIF}
  6. interface
  7. uses
  8. Classes, SysUtils, Forms, ComCtrls, StdCtrls, Controls, ExtCtrls, Graphics,
  9. LazPaintType, BCButton, BCComboBox, BCTrackbarUpdown, LCVectorialFillControl;
  10. type
  11. { TDarkTheme }
  12. TDarkTheme = class
  13. private
  14. FLastSystemDarkTheme: boolean;
  15. public
  16. constructor Create;
  17. procedure PanelPaint(Sender: TObject; ADarkTheme: boolean);
  18. procedure PanelPaintDark(Sender: TObject);
  19. procedure PanelPaintLight(Sender: TObject);
  20. procedure ToolBarPaint(Sender: TObject; ADarkTheme: boolean);
  21. procedure ToolBarPaintLight(Sender: TObject);
  22. procedure ToolBarPaintDark(Sender: TObject);
  23. procedure ToolBarPaintButton(Sender: TToolButton; State: integer; {%H-}ADarkTheme: boolean);
  24. procedure ToolBarPaintButtonLight(Sender: TToolButton; State: integer);
  25. procedure ToolBarPaintButtonDark(Sender: TToolButton; State: integer);
  26. procedure Apply(AForm: TForm; AThemeEnabled: boolean; ARecursive: boolean = true); overload;
  27. procedure Apply(APanel: TPanel; AThemeEnabled: boolean; ARecursive: boolean = true); overload;
  28. procedure Apply(AVectorialFill: TLCVectorialFillControl; AThemeEnabled: boolean); overload;
  29. procedure Apply(AToolbar: TToolbar; AThemeEnabled: boolean); overload;
  30. procedure Apply(AButton: TBCButton; ADarkTheme: boolean; AFontHeightRatio: single = 0.5); overload;
  31. procedure Apply(ACombo: TBCComboBox; ADarkTheme: boolean; AFontHeightRatio: single = 0.5); overload;
  32. procedure Apply(AUpDown: TBCTrackbarUpdown; ADarkTheme: boolean); overload;
  33. procedure Apply(ALabel: TLabel; ADarkTheme: boolean); overload;
  34. function IsSystemDarkTheme: boolean;
  35. function IsLclDarkTheme: boolean;
  36. function IsLclLightThemeSafe: boolean;
  37. function HasSystemDarkThemeChanged: boolean;
  38. function GetColorButtonHighlight(ADarkTheme: boolean): TColor;
  39. function GetColorButtonFace(ADarkTheme: boolean): TColor;
  40. function GetColorButtonText(ADarkTheme: boolean): TColor;
  41. function GetColorForm(ADarkTheme: boolean): TColor;
  42. function GetColorEditableFace(ADarkTheme: boolean): TColor;
  43. function GetColorEditableText(ADarkTheme: boolean): TColor;
  44. function GetColorPanelHighlight(ADarkTheme: boolean): TColor;
  45. function GetColorPanelShadow(ADarkTheme: boolean): TColor;
  46. function GetColorHighlightBack(ADarkTheme: boolean): TColor;
  47. function GetColorHighlightText(ADarkTheme: boolean): TColor;
  48. end;
  49. var
  50. DarkThemeInstance: TDarkTheme;
  51. implementation
  52. uses
  53. BCTypes, BGRABitmap, BGRABitmapTypes, GraphType, BGRACustomDrawn, LCScaleDPI
  54. {$IFDEF DARWIN_DARK_THEME}, CocoaAll, CocoaUtils{$ENDIF}
  55. {$IFDEF WINDOWS}, Win32Proc, Registry{$ENDIF};
  56. const
  57. clDarkBtnHighlight = $e0e0e0;
  58. clDarkBtnFace = $606060;
  59. clDarkEditableFace = $808080;
  60. clLightText = $f0f0f0;
  61. clDarkPanelHighlight = $909090;
  62. clDarkPanelShadow = $404040;
  63. {$IFDEF DARWIN_DARK_THEME}
  64. { returns true, if this app runs on macOS 10.14 Mojave or newer }
  65. function IsMojaveOrNewer: boolean;
  66. var
  67. minOsVer: NSOperatingSystemVersion;
  68. begin
  69. // Setup minimum version (Mojave)
  70. minOsVer.majorVersion:= 10;
  71. minOsVer.minorVersion:= 14;
  72. minOsVer.patchVersion:= 0;
  73. // Check minimum version
  74. if NSProcessInfo.ProcessInfo.isOperatingSystemAtLeastVersion(minOSVer) then
  75. Result := True
  76. else
  77. Result := False;
  78. end;
  79. function GetPrefString(const KeyName : string) : string;
  80. begin
  81. Result := NSStringToString(NSUserDefaults.standardUserDefaults.stringForKey(NSStr(@KeyName[1])));
  82. end;
  83. function IsMojaveDarkTheme: boolean;
  84. begin
  85. Result := pos('DARK',UpperCase(GetPrefString('AppleInterfaceStyle'))) > 0;
  86. end;
  87. {$ENDIF}
  88. {$IFDEF WINDOWS}
  89. type
  90. TWinDarkThemeMode = (dtmLight, dtmDark, dtmUnknown);
  91. // by "jwdietrich" from Lazarus forum
  92. // IsDarkTheme: Detects if the Dark Theme (true) has been enabled or not (false)
  93. function GetWinDarkTheme: TWinDarkThemeMode;
  94. const
  95. KEYPATH = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
  96. KEYNAME = 'AppsUseLightTheme';
  97. var
  98. Registry: TRegistry;
  99. begin
  100. Result := dtmUnknown;
  101. Registry := TRegistry.Create;
  102. try
  103. Registry.RootKey := HKEY_CURRENT_USER;
  104. if Registry.OpenKeyReadOnly(KEYPATH) then
  105. begin
  106. if Registry.ValueExists(KEYNAME) then
  107. begin
  108. if Registry.ReadBool(KEYNAME) then
  109. result := dtmLight
  110. else result := dtmDark;
  111. end;
  112. end;
  113. finally
  114. Registry.Free;
  115. end;
  116. end;
  117. {$ENDIF}
  118. procedure BCAssignSystemState(AState: TBCButtonState; AFontColor, ATopColor, AMiddleTopColor, AMiddleBottomColor, ABottomColor, ABorderColor: TColor);
  119. var middleColor: TColor;
  120. begin
  121. with AState do
  122. begin
  123. Border.Style := bboSolid;
  124. Border.Color := ABorderColor;
  125. Border.ColorOpacity := 192;
  126. FontEx.Color := AFontColor;
  127. FontEx.Style := [];
  128. FontEx.Shadow := True;
  129. FontEx.ShadowColor := clBlack;
  130. FontEx.ShadowColorOpacity := 192;
  131. FontEx.ShadowOffsetX := 1;
  132. FontEx.ShadowOffsetY := 1;
  133. FontEx.ShadowRadius := 2;
  134. middleColor := MergeBGRA(AMiddleTopColor.ToExpandedPixel, AMiddleBottomColor.ToExpandedPixel);
  135. FontEx.DisabledColor := MergeBGRA(middleColor.ToExpandedPixel, AFontColor.ToExpandedPixel);
  136. Background.Gradient1EndPercent := 60;
  137. Background.Style := bbsGradient;
  138. // Gradient1
  139. with Background.Gradient1 do
  140. begin
  141. GradientType := gtLinear;
  142. StartColor := ATopColor;
  143. EndColor := AMiddleTopColor;
  144. Point1XPercent := 0;
  145. Point1YPercent := 0;
  146. Point2XPercent := 0;
  147. Point2YPercent := 100;
  148. end;
  149. // Gradient2
  150. with Background.Gradient2 do
  151. begin
  152. StartColor := AMiddleBottomColor;
  153. EndColor := ABottomColor;
  154. GradientType := gtLinear;
  155. Point1XPercent := 0;
  156. Point1YPercent := 0;
  157. Point2XPercent := 0;
  158. Point2YPercent := 100;
  159. end;
  160. end;
  161. end;
  162. { TDarkTheme }
  163. constructor TDarkTheme.Create;
  164. begin
  165. FLastSystemDarkTheme := IsSystemDarkTheme;
  166. end;
  167. procedure TDarkTheme.PanelPaint(Sender: TObject; ADarkTheme: boolean);
  168. var
  169. c: TCanvas;
  170. begin
  171. if Sender is TCustomControl then
  172. begin
  173. c := TCustomControl(Sender).Canvas;
  174. c.Pen.Color := GetColorPanelHighlight(ADarkTheme);
  175. c.Line(0, 0, c.Width, 0);
  176. c.Line(0, 0, 0, c.Height);
  177. c.Pen.Color := GetColorPanelShadow(ADarkTheme);
  178. c.Line(0, c.Height-1, c.Width, c.Height-1);
  179. c.Line(c.Width-1, 0, c.Width-1, c.Height);
  180. end;
  181. end;
  182. procedure TDarkTheme.PanelPaintDark(Sender: TObject);
  183. begin
  184. PanelPaint(Sender, true);
  185. end;
  186. procedure TDarkTheme.PanelPaintLight(Sender: TObject);
  187. begin
  188. PanelPaint(Sender, false);
  189. end;
  190. procedure TDarkTheme.ToolBarPaint(Sender: TObject; ADarkTheme: boolean);
  191. var
  192. T: TToolBar;
  193. begin
  194. if Sender is TToolBar then
  195. begin
  196. T := TToolBar(Sender);
  197. if T.Align = alLeft then
  198. begin
  199. T.Canvas.Pen.Color := GetColorPanelShadow(ADarkTheme);
  200. T.Canvas.Line(T.Width-1, 0, T.Width-1, T.Height)
  201. end
  202. else if T.Align = alRight then
  203. begin
  204. T.Canvas.Pen.Color := GetColorPanelHighlight(ADarkTheme);
  205. T.Canvas.Line(0, 0, 0, T.Height)
  206. end
  207. else
  208. begin
  209. T.Canvas.Pen.Color := GetColorPanelShadow(ADarkTheme);
  210. T.Canvas.Line(0, 0, T.Width, 0);
  211. end;
  212. end;
  213. end;
  214. procedure TDarkTheme.ToolBarPaintLight(Sender: TObject);
  215. begin
  216. ToolbarPaint(Sender, false);
  217. end;
  218. procedure TDarkTheme.ToolBarPaintDark(Sender: TObject);
  219. begin
  220. ToolbarPaint(Sender, true);
  221. end;
  222. procedure TDarkTheme.ToolBarPaintButton(Sender: TToolButton; State: integer;
  223. ADarkTheme: boolean);
  224. var
  225. Bitmap: TBGRABitmap;
  226. //ts: TSize;
  227. T: TToolBar;
  228. imgW, imgH: integer;
  229. imgS: TGraphicsDrawEffect;
  230. begin
  231. Bitmap := nil;
  232. if Sender.Style in[tbsButton,tbsCheck] then
  233. begin
  234. if Sender.Enabled then
  235. begin
  236. if (State = 3) or Sender.Down then
  237. begin
  238. { Button Down }
  239. Bitmap := TBGRABitmap.Create(Sender.Width, Sender.Height);
  240. Bitmap.Rectangle(0, 0, Sender.Width, Sender.Height - 1,
  241. GetColorPanelShadow(ADarkTheme), dmSet);
  242. Bitmap.Rectangle(1, 1, Sender.Width - 1, Sender.Height - 2,
  243. MergeBGRA(ColorToBGRA(GetColorPanelShadow(ADarkTheme)), 2, ColorToBGRA(GetColorButtonFace(ADarkTheme)), 1),
  244. MergeBGRA(ColorToBGRA(GetColorPanelShadow(ADarkTheme)), 1, ColorToBGRA(GetColorButtonFace(ADarkTheme)), 2), dmSet);
  245. Bitmap.SetHorizLine(0, Sender.Height - 1, Sender.Width - 1,
  246. GetColorButtonFace(ADarkTheme));
  247. end
  248. else
  249. begin
  250. if State = 2 then
  251. begin
  252. { Button Hovered }
  253. Bitmap := TBGRABitmap.Create(Sender.Width, Sender.Height);
  254. Bitmap.GradientFill(0, 0, Sender.Width, Sender.Height, GetColorPanelHighlight(ADarkTheme),
  255. GetColorButtonFace(ADarkTheme), gtLinear, PointF(0, 0),
  256. PointF(0, Sender.Height), dmSet);
  257. Bitmap.Rectangle(0, 0, Sender.Width, Sender.Height - 1,
  258. GetColorPanelShadow(ADarkTheme),
  259. dmSet);
  260. Bitmap.SetHorizLine(1, 1, Sender.Width - 2,
  261. MergeBGRA(ColorToBGRA(GetColorPanelHighlight(ADarkTheme)), ColorToBGRA(GetColorButtonHighlight(ADarkTheme))));
  262. Bitmap.SetHorizLine(0, Sender.Height - 1, Sender.Width - 1,
  263. MergeBGRA(ColorToBGRA(GetColorPanelShadow(ADarkTheme)), ColorToBGRA(GetColorButtonFace(ADarkTheme))));
  264. end
  265. else
  266. { Button Normal }
  267. //Bitmap.Fill(BGRA(83, 83, 83));
  268. end;
  269. end
  270. else
  271. begin
  272. { Button Disabled }
  273. {Bitmap.Rectangle(0, 0, Sender.Width, Sender.Height - 1, BGRA(66, 66, 66),
  274. BGRA(71, 71, 71), dmSet);
  275. Bitmap.SetHorizLine(0, Sender.Height - 1, Sender.Width - 1, BGRA(83, 83, 83));}
  276. end;
  277. {Bitmap.FontName := Sender.Font.Name;
  278. Bitmap.FontStyle := Sender.Font.Style;
  279. Bitmap.FontHeight := Sender.Font.Height;
  280. Bitmap.FontQuality := fqSystemClearType;
  281. ts := Bitmap.TextSize(Sender.Caption);
  282. if Sender.Enabled then
  283. begin
  284. // Text Enabled
  285. Bitmap.TextOut((Sender.Width - ts.cx) div 2, ((Sender.Height - ts.cy) div 2) -
  286. 1, Sender.Caption, BGRA(47, 47, 47));
  287. Bitmap.TextOut((Sender.Width - ts.cx) div 2, (Sender.Height - ts.cy) div 2,
  288. Sender.Caption, BGRA(229, 229, 229));
  289. end
  290. else
  291. // Text Disabled
  292. Bitmap.TextOut((Sender.Width - ts.cx) div 2, (Sender.Height - ts.cy) div 2,
  293. Sender.Caption, BGRA(170, 170, 170));}
  294. end;
  295. if Assigned(Bitmap) then
  296. begin
  297. Bitmap.Draw(Sender.Canvas, 0, 0, False);
  298. Bitmap.Free;
  299. end;
  300. if (Sender.Parent is TToolBar) and
  301. (Sender.Style in [tbsButton,tbsButtonDrop,tbsCheck]) then
  302. begin
  303. T := TToolBar(Sender.Parent);
  304. if Assigned(T.Images) then
  305. begin
  306. imgW := T.Images.Width;
  307. imgH := T.Images.Height;
  308. if Sender.Enabled then
  309. imgS := gdeNormal
  310. else
  311. imgS := gdeDisabled;
  312. T.Images.Draw(Sender.Canvas, (Sender.Width - imgW) div 2, (Sender.Height - imgH) div
  313. 2, Sender.ImageIndex, imgS);
  314. end;
  315. end;
  316. end;
  317. procedure TDarkTheme.ToolBarPaintButtonLight(Sender: TToolButton; State: integer);
  318. begin
  319. ToolBarPaintButton(Sender, State, false);
  320. end;
  321. procedure TDarkTheme.ToolBarPaintButtonDark(Sender: TToolButton; State: integer);
  322. begin
  323. ToolBarPaintButton(Sender, State, true);
  324. end;
  325. procedure TDarkTheme.Apply(AForm: TForm; AThemeEnabled: boolean; ARecursive: boolean);
  326. var
  327. i: Integer;
  328. begin
  329. AForm.Color := GetColorButtonFace(AThemeEnabled);
  330. if ARecursive then
  331. for i := 0 to AForm.ControlCount-1 do
  332. begin
  333. if AForm.Controls[i] is TPanel then
  334. Apply(TPanel(AForm.Controls[i]), AThemeEnabled) else
  335. if AForm.Controls[i] is TToolBar then
  336. Apply(TToolBar(AForm.Controls[i]), AThemeEnabled) else
  337. if AForm.Controls[i] is TBCButton then
  338. Apply(TBCButton(AForm.Controls[i]), AThemeEnabled) else
  339. if AForm.Controls[i] is TLabel then
  340. Apply(TLabel(AForm.Controls[i]), AThemeEnabled);
  341. end;
  342. end;
  343. procedure TDarkTheme.Apply(APanel: TPanel; AThemeEnabled: boolean; ARecursive: boolean);
  344. var
  345. i: Integer;
  346. begin
  347. if AThemeEnabled then
  348. begin
  349. APanel.BevelOuter:= bvNone;
  350. if (APanel.OnPaint = nil) or (APanel.OnPaint = @PanelPaintLight) then
  351. APanel.OnPaint := @PanelPaintDark;
  352. end else
  353. begin
  354. if not IsLclLightThemeSafe then
  355. begin
  356. APanel.BevelOuter:= bvNone;
  357. APanel.OnPaint := @PanelPaintLight;
  358. end else
  359. begin
  360. APanel.BevelOuter:= bvRaised;
  361. if APanel.OnPaint = @PanelPaintDark then APanel.OnPaint := nil;
  362. end;
  363. end;
  364. APanel.Color := GetColorButtonFace(AThemeEnabled);
  365. if ARecursive then
  366. for i := 0 to APanel.ControlCount-1 do
  367. begin
  368. if APanel.Controls[i] is TPanel then
  369. Apply(TPanel(APanel.Controls[i]), AThemeEnabled) else
  370. if APanel.Controls[i] is TToolBar then
  371. Apply(TToolBar(APanel.Controls[i]), AThemeEnabled) else
  372. if APanel.Controls[i] is TBCButton then
  373. Apply(TBCButton(APanel.Controls[i]), AThemeEnabled) else
  374. if APanel.Controls[i] is TBCComboBox then
  375. Apply(TBCComboBox(APanel.Controls[i]), AThemeEnabled) else
  376. if APanel.Controls[i] is TBCTrackbarUpdown then
  377. Apply(TBCTrackbarUpdown(APanel.Controls[i]), AThemeEnabled) else
  378. if APanel.Controls[i] is TLabel then
  379. Apply(TLabel(APanel.Controls[i]), AThemeEnabled) else
  380. if APanel.Controls[i] is TLCVectorialFillControl then
  381. Apply(TLCVectorialFillControl(APanel.Controls[i]), AThemeEnabled);
  382. end;
  383. end;
  384. procedure TDarkTheme.Apply(AVectorialFill: TLCVectorialFillControl;
  385. AThemeEnabled: boolean);
  386. var
  387. i: Integer;
  388. begin
  389. AVectorialFill.Color := GetColorButtonFace(AThemeEnabled);
  390. for i := 0 to AVectorialFill.ControlCount-1 do
  391. begin
  392. if AVectorialFill.Controls[i] is TPanel then
  393. Apply(TPanel(AVectorialFill.Controls[i]), AThemeEnabled) else
  394. if AVectorialFill.Controls[i] is TToolBar then
  395. Apply(TToolBar(AVectorialFill.Controls[i]), AThemeEnabled) else
  396. if AVectorialFill.Controls[i] is TBCButton then
  397. Apply(TBCButton(AVectorialFill.Controls[i]), AThemeEnabled) else
  398. if AVectorialFill.Controls[i] is TLabel then
  399. Apply(TLabel(AVectorialFill.Controls[i]), AThemeEnabled);
  400. end;
  401. end;
  402. procedure TDarkTheme.Apply(AToolbar: TToolbar; AThemeEnabled: boolean);
  403. var
  404. i: Integer;
  405. begin
  406. if AThemeEnabled then
  407. begin
  408. if (AToolbar.OnPaintButton = nil) or (AToolbar.OnPaintButton = @ToolBarPaintButtonLight) then
  409. AToolbar.OnPaintButton := @ToolBarPaintButtonDark;
  410. end else
  411. begin
  412. if not IsLclLightThemeSafe then AToolbar.OnPaintButton := @ToolBarPaintButtonLight else
  413. if AToolbar.OnPaintButton = @ToolBarPaintButtonDark then AToolbar.OnPaintButton := nil;
  414. end;
  415. AToolbar.Color := GetColorButtonFace(AThemeEnabled);
  416. for i := 0 to AToolbar.ControlCount-1 do
  417. begin
  418. if AToolbar.Controls[i] is TBCButton then
  419. Apply(TBCButton(AToolbar.Controls[i]), AThemeEnabled, 0.55) else
  420. if AToolbar.Controls[i] is TBCComboBox then
  421. Apply(TBCComboBox(AToolbar.Controls[i]), AThemeEnabled, 0.55) else
  422. if AToolbar.Controls[i] is TBCTrackbarUpdown then
  423. Apply(TBCTrackbarUpdown(AToolbar.Controls[i]), AThemeEnabled);
  424. end;
  425. end;
  426. procedure TDarkTheme.Apply(AButton: TBCButton; ADarkTheme: boolean; AFontHeightRatio: single);
  427. function MergeColor(AColor1,AColor2:TColor):TColor;
  428. begin
  429. result:= BGRAToColor(MergeBGRAWithGammaCorrection(ColorToBGRA(ColorToRGB(AColor1)),1,
  430. ColorToBGRA(ColorToRGB(AColor2)),1));
  431. end;
  432. function HoverColor(AColor1: TColor): TColor;
  433. var hsla1, hsla2: THSLAPixel;
  434. begin
  435. hsla1 := BGRAToHSLA(ColorToBGRA(ColorToRGB(AColor1)));
  436. hsla2 := BGRAToHSLA(ColorToBGRA(ColorToRGB(clHighlight)));
  437. hsla1.hue := hsla2.hue;
  438. hsla1.saturation:= hsla2.saturation;
  439. result := BGRAToColor(HSLAToBGRA(hsla1));
  440. end;
  441. var highlight, btnFace, btnShadow, btnText, gradMiddle: TColor;
  442. fh: Int64;
  443. begin
  444. if ADarkTheme then
  445. begin
  446. highlight := $a0a0a0;
  447. end else
  448. begin
  449. {$IFDEF DARWIN}
  450. highlight := MergeColor(GetColorButtonFace(false),clWhite);
  451. {$ELSE}
  452. highlight := GetColorButtonHighlight(false);
  453. {$ENDIF}
  454. end;
  455. btnFace := GetColorButtonFace(ADarkTheme);
  456. btnText := GetColorButtonText(ADarkTheme);
  457. btnShadow := GetColorPanelShadow(ADarkTheme);
  458. gradMiddle := MergeColor(btnFace,highlight);
  459. with AButton do
  460. begin
  461. Rounding.RoundX := DoScaleX(3, OriginalDPI);
  462. Rounding.RoundY := DoScaleX(3, OriginalDPI);
  463. BCAssignSystemState(StateNormal, btnText, btnFace, highlight,
  464. gradMiddle, btnShadow, btnShadow);
  465. BCAssignSystemState(StateHover, HoverColor(btnText), HoverColor(btnFace), HoverColor(highlight),
  466. HoverColor(gradMiddle), HoverColor(btnShadow), HoverColor(btnShadow));
  467. BCAssignSystemState(StateClicked, HoverColor(btnText), HoverColor(MergeColor(btnFace,btnShadow)),
  468. HoverColor(btnFace), HoverColor(MergeColor(btnFace,btnShadow)), HoverColor(btnShadow), HoverColor(btnShadow));
  469. fh := round((AButton.Height+4)*AFontHeightRatio);
  470. StateNormal.Border.LightWidth := 0;
  471. StateNormal.FontEx.Height := fh;
  472. StateNormal.FontEx.ShadowColorOpacity:= 70;
  473. StateNormal.FontEx.TextAlignment:= bcaLeftCenter;
  474. StateNormal.FontEx.PaddingLeft:= DoScaleX(3, OriginalDPI);
  475. StateHover.Border.LightWidth := 0;
  476. StateHover.FontEx.Height := fh;
  477. StateHover.FontEx.ShadowColorOpacity:= 70;
  478. StateHover.FontEx.TextAlignment:= bcaLeftCenter;
  479. StateHover.FontEx.PaddingLeft:= DoScaleX(3, OriginalDPI);
  480. StateClicked.Border.LightWidth := 0;
  481. StateClicked.FontEx.Height := fh;
  482. StateClicked.FontEx.ShadowColorOpacity:= 70;
  483. StateClicked.FontEx.TextAlignment:= bcaLeftCenter;
  484. StateClicked.FontEx.PaddingLeft:= DoScaleX(3, OriginalDPI);
  485. GlyphMargin := DoScaleX(3, OriginalDPI);
  486. end;
  487. end;
  488. procedure TDarkTheme.Apply(ACombo: TBCComboBox; ADarkTheme: boolean;
  489. AFontHeightRatio: single);
  490. var
  491. fh: Int64;
  492. begin
  493. Apply(ACombo.Button, ADarkTheme, AFontHeightRatio);
  494. with ACombo do
  495. begin
  496. FocusBorderOpacity:= 128;
  497. fh := round((Height+4)*AFontHeightRatio);
  498. Button.StateNormal.FontEx.Height := fh;
  499. Button.StateNormal.FontEx.ShadowColorOpacity:= 96;
  500. Button.StateClicked.FontEx.Height := fh;
  501. Button.StateClicked.FontEx.ShadowColorOpacity:= 96;
  502. Button.StateHover.FontEx.Height := fh;
  503. Button.StateHover.FontEx.ShadowColorOpacity:= 96;
  504. DropDownFontColor:= GetColorEditableText(ADarkTheme);
  505. DropDownColor:= GetColorEditableFace(ADarkTheme);
  506. FocusBorderColor:= GetColorEditableText(ADarkTheme);
  507. DropDownBorderColor:= MergeBGRA(ColorToBGRA(DropDownFontColor), ColorToBGRA(DropDownColor));
  508. DropDownFontHighlight:= GetColorHighlightText(ADarkTheme);
  509. DropDownHighlight:= GetColorHighlightBack(ADarkTheme);
  510. end;
  511. end;
  512. procedure TDarkTheme.Apply(AUpDown: TBCTrackbarUpdown; ADarkTheme: boolean);
  513. begin
  514. if ADarkTheme then
  515. AUpDown.ButtonBackground.Color:= $a0a0a0
  516. else
  517. AUpDown.ButtonBackground.Color:= GetColorButtonFace(ADarkTheme);
  518. AUpDown.Border.Color := GetColorPanelShadow(ADarkTheme);
  519. AUpDown.Background.Color := GetColorEditableFace(ADarkTheme);
  520. AUpDown.ButtonBackground.Style:= bbsColor;
  521. AUpDown.Font.Color := GetColorEditableText(ADarkTheme);
  522. end;
  523. procedure TDarkTheme.Apply(ALabel: TLabel; ADarkTheme: boolean);
  524. begin
  525. ALabel.Font.Color := GetColorEditableText(ADarkTheme);
  526. end;
  527. function TDarkTheme.IsSystemDarkTheme: boolean;
  528. begin
  529. {$IFDEF DARWIN_DARK_THEME}
  530. if IsMojaveOrNewer then
  531. exit(IsMojaveDarkTheme);
  532. {$ENDIF}
  533. {$IFDEF WINDOWS}
  534. case GetWinDarkTheme of
  535. dtmLight: exit(false);
  536. dtmDark: exit(true);
  537. end;
  538. {$ENDIF}
  539. result := IsLclDarkTheme;
  540. end;
  541. function TDarkTheme.IsLclDarkTheme: boolean;
  542. const
  543. cMax = $A0;
  544. var
  545. N: TColor;
  546. begin
  547. N:= ColorToRGB(clWindow);
  548. Result:= (Red(N)<cMax) and (Green(N)<cMax) and (Blue(N)<cMax);
  549. end;
  550. function TDarkTheme.IsLclLightThemeSafe: boolean;
  551. begin
  552. result := not IsLclDarkTheme and not IsSystemDarkTheme;
  553. end;
  554. function TDarkTheme.HasSystemDarkThemeChanged: boolean;
  555. var
  556. newState: Boolean;
  557. begin
  558. newState := IsSystemDarkTheme;
  559. if newState <> FLastSystemDarkTheme then
  560. begin
  561. result := true;
  562. FLastSystemDarkTheme:= newState;
  563. end else
  564. result := false;
  565. end;
  566. function TDarkTheme.GetColorButtonHighlight(ADarkTheme: boolean): TColor;
  567. begin
  568. if ADarkTheme then result := clDarkBtnHighlight
  569. else if not IsLclLightThemeSafe then result := $f0f0f0
  570. else result := clBtnHighlight;
  571. end;
  572. function TDarkTheme.GetColorButtonFace(ADarkTheme: boolean): TColor;
  573. begin
  574. if ADarkTheme then result := clDarkBtnFace
  575. else if not IsLclLightThemeSafe then result := $d8d8d8
  576. else result := clBtnFace;
  577. end;
  578. function TDarkTheme.GetColorForm(ADarkTheme: boolean): TColor;
  579. begin
  580. if ADarkTheme then result := clDarkBtnFace
  581. else if not IsLclLightThemeSafe then result := $d8d8d8
  582. else result := clForm;
  583. end;
  584. function TDarkTheme.GetColorEditableFace(ADarkTheme: boolean): TColor;
  585. begin
  586. if ADarkTheme then result := clDarkEditableFace
  587. else if not IsLclLightThemeSafe then result := $ffffff
  588. else result := clWindow;
  589. end;
  590. function TDarkTheme.GetColorEditableText(ADarkTheme: boolean): TColor;
  591. begin
  592. if ADarkTheme then result := clLightText
  593. else if not IsLclLightThemeSafe then result := $303030
  594. else result := clWindowText;
  595. end;
  596. function TDarkTheme.GetColorButtonText(ADarkTheme: boolean): TColor;
  597. begin
  598. if ADarkTheme then result := clLightText
  599. else if not IsLclLightThemeSafe then result := clBlack
  600. else result := clBtnText;
  601. end;
  602. function TDarkTheme.GetColorPanelHighlight(ADarkTheme: boolean): TColor;
  603. begin
  604. if ADarkTheme then result := clDarkPanelHighlight
  605. else if not IsLclLightThemeSafe then result := $f0f0f0
  606. else result := clBtnHighlight;
  607. end;
  608. function TDarkTheme.GetColorPanelShadow(ADarkTheme: boolean): TColor;
  609. begin
  610. if ADarkTheme then result := clDarkPanelShadow
  611. else if not IsLclLightThemeSafe then result := $808080
  612. else result := clBtnShadow;
  613. end;
  614. function TDarkTheme.GetColorHighlightBack(ADarkTheme: boolean): TColor;
  615. begin
  616. if ADarkTheme then
  617. begin
  618. if BGRADiff(ColorToBGRA(clHighlight), clDarkBtnFace)>=64 then
  619. result := clHighlight
  620. else result := MergeBGRA(ColorToBGRA(clLightText), ColorToBGRA(clHighlight));
  621. end
  622. else result := clHighlight;
  623. end;
  624. function TDarkTheme.GetColorHighlightText(ADarkTheme: boolean): TColor;
  625. begin
  626. if ADarkTheme then
  627. begin
  628. if BGRADiff(ColorToBGRA(clHighlight), clDarkBtnFace)>=64 then
  629. result := clHighlightText
  630. else result := clBlack;
  631. end
  632. else result := clHighlightText;
  633. end;
  634. initialization
  635. DarkThemeInstance := TDarkTheme.Create;
  636. finalization
  637. DarkThemeInstance.Free;
  638. end.