Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dark theme for Windows #1300

Open
ghost opened this issue Mar 31, 2020 · 10 comments
Open

Dark theme for Windows #1300

ghost opened this issue Mar 31, 2020 · 10 comments

Comments

@ghost
Copy link

ghost commented Mar 31, 2020

Please add the dark theme for Windows.

@leonsoft-kras
Copy link
Contributor

Implement native support in the development environment (Lazarus), suggest changes to the program code. Offer your help. Welcome.

@leonsoft-kras leonsoft-kras pinned this issue Apr 16, 2020
@LeVraiRoiDHyrule
Copy link

Yes please, a dark theme for Windows is the only thing missing in this wonderful remote

@Terrails
Copy link

Terrails commented May 28, 2021

There seems to be a way to detect if windows is using Dark/Light mode here although there's still no native support implemented in Lazarus itself. Pretty sad that this has been going on for years with no native support yet.

@UltimateByte
Copy link

@FluxState
Copy link

FluxState commented Jan 29, 2022

Even if we manually update every color in the app with appropriate auto-detected dark/light palette - a lot of the default widgets just don't support customization and they'd still be bright or have light borders.

I fear that without replacing default widgets (LCL components) with sth like BGRAControls this won't be possible and it's better to focus on doing Win builds of transmission-remote-gtk.

This is what I've done so far with auto-detected win theme:
2022-01-29 21_12_41-Transmission Remote GUI v5 18 0

AutoColors.pas

{*************************************************************************************
  This file is part of Transmission Remote GUI.
  Copyright (c) 2008-2019 by Yury Sidorov and Transmission Remote GUI working group.

  Transmission Remote GUI is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  Transmission Remote GUI is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Transmission Remote GUI; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

  In addition, as a special exception, the copyright holders give permission to
  link the code of portions of this program with the
  OpenSSL library under certain conditions as described in each individual
  source file, and distribute linked combinations including the two.

  You must obey the GNU General Public License in all respects for all of the
  code used other than OpenSSL.  If you modify file(s) with this exception, you
  may extend this exception to your version of the file(s), but you are not
  obligated to do so.  If you do not wish to do so, delete this exception
  statement from your version.  If you delete this exception statement from all
  source files in the program, then also delete it here.

  Unit authors:
    FluxState
    jwdietrich
    prof7bit

*************************************************************************************}

unit AutoColors;

{$mode ObjFPC}{$H+}

interface

uses
  {$ifdef windows}
  Registry, Windows, Win32Proc,
  {$endif windows}
  Graphics;

const
  // default color names from the VGA standard,
  // will stay unmodified on light themes
  clAutoAqua: TColor = clAqua;
  clAutoBlack: TColor = clBlack;
  clAutoBlue: TColor = clBlue;
  clAutoFuchsia: TColor = clFuchsia;
  clAutoGray: TColor = clGray;
  clAutoGreen: TColor = clGreen;
  clAutoLime: TColor = clLime;
  clAutoMaroon: TColor = clMaroon;
  clAutoNavy: TColor = clNavy;
  clAutoOlive: TColor = clOlive;
  clAutoPurple: TColor = clPurple;
  clAutoRed: TColor = clRed;
  clAutoSilver: TColor = clSilver;
  clAutoTeal: TColor = clTeal;
  clAutoWhite: TColor = clWhite;
  clAutoYellow: TColor = clYellow;

  // widgets
  clWindowText20: TColor = clNone;
  clWindowText40: TColor = clNone;
  clWindowText60: TColor = clNone;
  clWindowText80: TColor = clNone;

  clWindow20: TColor = clNone;
  clWindow40: TColor = clNone;
  clWindow60: TColor = clNone;
  clWindow80: TColor = clNone;

  clAutoBtnFace: TColor = clBtnFace;
  clAutoBtnShadow: TColor = clBtnShadow;
  clAutoDefault: TColor = clDefault;
  clAutoHighlight: TColor = clHighlight;
  clAutoHighlightText: TColor = clHighlightText;
  clAutoMenuText: TColor = clMenuText;
  clAutoWindow: TColor = clWindow;
  clAutoWindowText: TColor = clWindowText;

type
  TThemeForce = (
    forceDark,
    forceLight,
    forceAuto
  );

function Mix(A, B: TColor; Ratio: Integer): TColor;
function IsDarkTheme: Boolean;
function isDarkThemeWindows: Boolean;
procedure UpdateThemeColors(Force: TThemeForce=forceAuto);

implementation

function Mix(A, B: TColor; Ratio: Integer): TColor;
var
  RgbA, RgbB: LongInt;
  Compl: Integer;
begin
  RgbA := ColorToRGB(A);
  RgbB := ColorToRGB(B);
  Compl := 10 - Ratio;
  Result := RGBToColor(
    (Red(RgbA)   * Compl + Red(RgbB)   * Ratio) div 10,
    (Green(RgbA) * Compl + Green(RgbB) * Ratio) div 10,
    (Blue(RgbA)  * Compl + Blue(RgbB)  * Ratio) div 10
  );
end;

function IsDarkTheme: Boolean;
var
  ColW, ColT: LongInt;
begin
  ColW := ColorToRGB(clWindow);
  ColT := ColorToRGB(clWindowText);
  Result := Red(ColW) + Green(ColW) + Blue(ColW) < Red(ColT) + Green(ColT) + Blue(ColT);
end;

function IsDarkThemeWindows: boolean;
const
  KEYPATH = '\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize';
  KEYNAME = 'AppsUseLightTheme';
var
  LightKey: boolean;
  Registry: TRegistry;
begin
  Result := false;
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if Registry.OpenKeyReadOnly(KEYPATH) then
      begin
        if Registry.ValueExists(KEYNAME) then
          LightKey := Registry.ReadBool(KEYNAME)
        else
          LightKey := true;
      end
    else
      LightKey := true;
    Result := not LightKey
  finally
    Registry.Free;
  end;
end;

procedure UpdateThemeColors(Force: TThemeForce);
var
  Dark: Boolean;
begin
  case Force of
    forceDark: Dark := True;
    forceLight: Dark := False;
    {$ifdef windows}
    forceAuto: Dark := IsDarkThemeWindows;
    {$else}
    forceAuto: Dark := IsDarkTheme;
    {$endif}
  end;

  clWindowText20 := Mix(clWindow, clWindowText, 2);
  clWindowText40 := Mix(clWindow, clWindowText, 4);
  clWindowText60 := Mix(clWindow, clWindowText, 6);
  clWindowText80 := Mix(clWindow, clWindowText, 8);

  clWindow20 := Mix(clWindowText, clWindow, 2);
  clWindow40 := Mix(clWindowText, clWindow, 4);
  clWindow60 := Mix(clWindowText, clWindow, 6);
  clWindow80 := Mix(clWindowText, clWindow, 8);

  if Dark then begin // tweaked for dark theme

    clAutoBlack := clWhite;
    clAutoWhite := clBlack;
    clAutoAqua := Mix(clAqua, clBlack, 5);
    clAutoLime := Mix(clGreen, clYellow, 4);
    clAutoSilver := Mix(clSilver, clBlack, 5);
    clAutoYellow := Mix(clYellow, clRed, 5);

    clAutoBlue := Mix(clBlue, clWhite, 6);
    clAutoNavy := Mix(clBlue, clWhite, 4);
    clAutoOlive := Mix(clOlive, clWhite, 2);
    clAutoGreen := Mix(clGreen, clWhite, 2);
    clAutoMaroon := Mix(Mix(clMaroon, clRed, 2), clWhite, 3);
    clAutoPurple := Mix(clPurple, clWhite, 5);
    clAutoTeal := Mix(clTeal, clWhite, 2);

    clAutoDefault := Mix(clBlack, clDefault, 2);
    clAutoHighlight := clAutoBlue;
    clAutoHighlightText := clBlack;
    clAutoMenuText := clWhite;
    clAutoWindow := clBlack;
    clAutoWindowText := clWhite;

  end
  else begin // defaults for light theme

    clAutoBlack := clBlack;
    clAutoWhite := clWhite;
    clAutoAqua := clAqua;
    clAutoLime := clLime;
    clAutoSilver := clSilver;
    clAutoYellow := clYellow;

    clAutoBlue := clBlue;
    clAutoNavy := clNavy;
    clAutoOlive := clOlive;
    clAutoGreen := clGreen;
    clAutoMaroon := clMaroon;
    clAutoPurple := clPurple;
    clAutoTeal := clTeal;    

    clAutoDefault := clDefault;   
    clAutoHighlight := clHighlight;
    clAutoHighlightText := clHighlightText;
    clAutoMenuText := clMenuText;
    clAutoWindow := clWindow;   
    clAutoWindowText := clWindowText;

  end;
end;

initialization
  UpdateThemeColors(TThemeForce.forceAuto);
end.

In Main.pas, e.g in UpdateUI:

  // AutoColors  

  MainForm.Color := clAutoDefault;
  MainForm.Font.Color := clAutoWindowText;

  panFilter.Color := clAutoDefault;  
  panFilter.Color := clAutoDefault;

  gTorrents.AlternateColor:=clAutoWindow;
  for i:=0 to gTorrents.Columns.Count - 1 do
    with gTorrents.Columns[i] do begin
       Color := clAutoWindow;
       Font.Color := clAutoWindowText;
    end;
  gTorrents.Color:=clAutoWindow;
  gTorrents.Font.Color := clAutoWindowText;

  HSplitter.Color := clAutoDefault;

  for i:=0 to lvFilter.Columns.Count - 1 do
    with lvFilter.Columns[i] do begin
       Color := clAutoWindow;
       Font.Color := clAutoWindowText;
    end;             
  lvFilter.Color:=clAutoWindow;
  lvFilter.Font.Color:=clAutoDefault;

  MainToolBar.Color := clAutoDefault;
  MainToolBar.Font.Color := clAutoWindowText;

  panSearch.BevelColor:=clAutoGray;
  panSearch.Color := clAutoDefault;

  edSearch.Color := clAutoDefault;
  edSearch.Font.Color := clAutoWindowText;

  SearchToolbar.Color := clAutoDefault;

  PageInfo.Color := clAutoDefault;
  PageInfo.Font.Color := clAutoWindowText;

  tabGeneral.Color:=clAutoDefault;
  tabGeneral.Font.Color := clAutoWindowText;

  txTransferHeader.BevelColor := clAutoDefault;
  txTransferHeader.Color := clAutoDefault;   
  txTransferHeader.Font.Color := clAutoWindowText;

  txTorrentHeader.BevelColor := clAutoDefault;
  txTorrentHeader.Color := clAutoDefault;
  txTorrentHeader.Font.Color := clAutoWindowText;

  txMagnetLink.Color := clAutoDefault;              
  txMagnetLink.Font.Color := clAutoWindowText;

  StatusBar.Color:=clAutoDefault;
  StatusBar.Font.Color := clAutoWindowText;

  VSplitter.Color := clAutoDefault;   

You can also set your win to a contrast theme. E.g. save this one as contrast.theme and open:

[Theme]
; Windows - IDS_THEME_DISPLAYNAME_AERO
DisplayName=contrast

[Control Panel\Desktop]
PicturePosition=4
MultimonBackgrounds=0
Wallpaper=

[VisualStyles]
Path=%SystemRoot%\Resources\Themes\Aero\AeroLite.msstyles
ColorStyle=NormalColor
Size=NormalSize
AutoColorization=0
ColorizationColor=0XC4253250
VisualStyleVersion=10
HighContrast=3
AppMode=Dark
SystemMode=Dark

[MasterThemeSelector]
MTSM=RJSPBS

[Control Panel\Colors]
ActiveBorder=45 45 45
ActiveTitle=45 45 45
AppWorkspace=45 45 45
Background=0 0 0
ButtonAlternateFace=192 192 192
ButtonDkShadow=45 45 45
ButtonFace=35 35 35
ButtonHilight=45 45 45
ButtonLight=45 45 45
ButtonShadow=128 128 128
ButtonText=220 220 220
GradientActiveTitle=192 192 192
GradientInactiveTitle=128 128 128
GrayText=128 128 128
Hilight=63 63 63
HilightText=252 252 252
HotTrackingColor=220 220 255
InactiveBorder=45 45 45
InactiveTitle=45 45 45
InactiveTitleText=192 192 192
InfoText=220 220 220
InfoWindow=45 45 45
Menu=45 45 45
MenuBar=45 45 45
MenuHilight=128 128 128
MenuText=230 230 230
Scrollbar=45 45 45
TitleText=220 220 220
Window=0 0 0
WindowFrame=15 15 15
WindowText=220 220 220

[Sounds]
SchemeName=No Sounds

[CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}\DefaultIcon]
DefaultValue=%SystemRoot%\System32\imageres.dll,-109
[CLSID\{59031A47-3F72-44A7-89C5-5595FE6B30EE}\DefaultIcon]
DefaultValue=%SystemRoot%\System32\imageres.dll,-123
[CLSID\{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}\DefaultIcon]
DefaultValue=%SystemRoot%\System32\imageres.dll,-25
[CLSID\{645FF040-5081-101B-9F08-00AA002F954E}\DefaultIcon]
full=%SystemRoot%\System32\imageres.dll,-54
empty=%SystemRoot%\System32\imageres.dll,-55

[Control Panel\Cursors]
Arrow=
Help=
Hand=
AppStarting=
Wait=
NWPen=
No=
SizeNS=
SizeWE=
Crosshair=
IBeam=
SizeNWSE=
SizeNESW=
SizeAll=
UpArrow=
``

@hetzjagd
Copy link

I'm so confused coming in to this in 2023. In the main Readme.md there is a screenshot with there being a great dark mode in use. Separate to that, how can a Windows user find AutoColors.pas and Main.pas and implement the changes above? Where do I find these files?

I see the "You can also set your win to a contrast theme. E.g. save this one as contrast.theme and open" which I assume is talking about a Windows theme, but my windows theming/colors is all the way right currently, I'm really apprehensive to try pursue that as a fix unless someone can tell me it's written with someone already using Windows 10 Dark Mode successfully and it's just a tweak to that existing color profile/theme.

@Mrnofish
Copy link

how can a Windows user find AutoColors.pas and Main.pas and implement the changes above? Where do I find these files?

.pas files are Pascal source code, Delphi, which Transgui is built upon, is an evolution of the Pascal programming language.

So I'm taking it, it's not simply a matter of editing a configuration file, for changes to source code requires some level of programming skills and being able to submit a PR to the project.

IOW if you have to ask, you might not be able to do it yourself. Transgui has not seen a new release in a few years, and while not abandoned, even if you were to submit a working patch, you'd not be able to use it unless you build it yourself.

There is, however, a fork by @xavery that's been actively worked on and has been releasing new builds.

@lighterowl
Copy link
Contributor

@hetzjagd Pretty much what @Mrnofish said. Unfortunately, there are no user serviceable parts here with regards to this.

As it stands now, dark themes are supported on both Linux and macOS. On Linux, this depends on your desktop environment's theme and the version of Transgui you're using (GTK or Qt5, my builds are Qt5 exclusively) - if your GTK/Qt5 theme is dark, Transgui adapts to it. The screenshot in this project's readme comes from a Linux GTK (from the looks of it) version.
In the meantime, this also seems to work more or less okay on macOS. Here's a screenshot of my latest build running on Big Sur : Screenshot_2023-04-16_12-19-59

I'll have a look but I can't promise anything, there must be something I'm missing about dark theme support on Windows and will have to read up. tl;dr I would expect the system to draw its controls dark-themed when the system is set to use it, similarly to what happens with Linux and macOS - as it just makes sense. However, for some reason this doesn't happen which might be how dark theme just works on Windows, i.e. applications seem to be required to do their own theming and themselves detect if dark theme is used or not.

@Mrnofish
Copy link

Re: dark theme on windows, from a purely end user pov, I'm afraid you hit the nail squarely on the head.

It's probably much different and easier for universal or whatever Microsoft calls "new style" windows apps these days, as those have UIs based on web-like technology (or this is my rough understanding of the matter).

With that said it should be possible to "back port" the GTK version to windows, and reuse the work done there on dark theming.

I have no idea how much work is that and what drawbacks the switch may entail, though.

If I had to take a guess, I'd say transgui would benefit more from core work on functionality, but thats just my opinion.

OTOH deploying dark theme on windows, despite being essentially cosmetic, is highly visible work that may attract attention and users. Paradoxically, upgrading openssl is "less exciting" stuff your average end user might not even care too much especially if they don't manage seed boxes over the open internet, but rather stick to vpns and local networks.

Just my two cents.

@lighterowl
Copy link
Contributor

Okay. Still very confused about all this. It looks like support for dark mode in Win32 applications is generally in a kind of a messy state where it's officially not supported but can actually be enabled thanks to the fantastic work done in https://github.com/ysc3839/win32-darkmode which uses undocumented APIs available in Windows 10 1809 and later.

However, after experimenting with the code in that repo it became clear that just calling InitDarkMode at application's initialisation isn't enough, and to make the whole thing work, additional changes in processing Windows messages related to initialising individual controls are needed. The MR adding dark mode support to Notepad++ seems to confirm this as well as it's almost 2K lines in total.

Anyhow, incorporating this into transgui would require the work of somebody who knows what they're doing in both Pascal (so the C++ code can be ported) and Windows (so any changes to message loops can be made). This effectively makes me completely unfit for this task, unfortunately.

With that said it should be possible to "back port" the GTK version to windows, and reuse the work done there on dark theming.

If the Windows version of GTK uses themes in a way similar to the Linux version, it could be done by "just" shipping the Windows-GTK version with a selectable dark theme file. However, if it does or how does one even begin to create a Windows-GTK version of a Lazarus application is completely unknown to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

8 participants